我想进入更多的模板元编程.我知道SFINAE代表"替换失败不是错误".但是有人能告诉我SFINAE的用处吗?
测试下面的代码,我把输出信息放在评论中.我使用的是gcc 4.8.5和Centos 7.2.
#include <iostream>
#include <cstdio>
class C
{
public:
void foo() {
printf("%p, %p\n", &C::c, &(C::c)); // output value is 0x4, 0x7ffc2e7f52e8
std::cout << &C::c << std::endl; // output value is 1
}
int a;
int c;
};
int main(void)
{
C co;
printf("%p\n", &C::c); // output value is 0x4
std::cout << &C::c << std::endl; // output value is 1
// printf("%p\n", &(C::c)); // compile error, invalid use of non-static data member 'C::c'
co.foo();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在 Visual Studio 中,成员变量的指针似乎在幕后是 32 位有符号整数(即使在 64 位模式下),并且在该上下文中空指针为 -1。所以如果我有一个类:
#include <iostream>
#include <cstdint>
struct Foo
{
char arr1[INT_MAX];
char arr2[INT_MAX];
char ch1;
char ch2;
};
int main()
{
auto p = &Foo::ch2;
std::cout << (p?"Not null":"null") << '\n';
}
Run Code Online (Sandbox Code Playgroud)
它编译并打印“null”。那么,是我造成了某种未定义的行为,还是编译器应该拒绝此代码而这是编译器中的错误?
编辑:
看来我可以保留“2 个INT_MAX数组加 2 个字符”模式,只有在这种情况下,编译器才允许我添加任意数量的成员,并且第二个字符始终被视为空值。见演示。如果我稍微改变了模式(比如在某个时候用 1 或 3 个字符而不是 2 个字符),它会抱怨这个类太大了。
我们假设我有一个这样的结构:
struct my_struct
{
int a;
int b;
}
Run Code Online (Sandbox Code Playgroud)
我有一个函数应该为"a"或"b"设置一个新值.此函数还需要指定要设置的变量.一个典型的例子是这样的:
void f(int which, my_struct* s, int new_value)
{
if(which == 0)
s->a = new_value;
else
s->b = new_value;
}
Run Code Online (Sandbox Code Playgroud)
由于我不会在这里写的原因,我无法将指针传递给a/b到f.所以我不能用my_struct :: a或my_struct :: b的地址调用f.我不能做的另一件事是在my_struct中声明一个向量(int vars [2])并将一个整数作为索引传递给f.基本上在f中我需要按名称访问变量.
以前的例子的问题是,在将来我计划向struct添加更多变量,在这种情况下,我将记得向f添加更多if语句,这对于可移植性是不利的.我能做的就是将f写为宏,如下所示:
#define FUNC(which)
void f(my_struct* s, int new_value) \
{ \
s->which = new_value; \
}
Run Code Online (Sandbox Code Playgroud)
然后我可以调用FUNC(a)或FUNC(b).
这可行,但我不喜欢使用宏.所以我的问题是:有没有办法使用模板而不是宏来实现相同的目标?
编辑:我将尝试解释为什么我不能使用指针,我需要按名称访问变量.基本上,结构包含系统的状态.该系统需要在请求时"撤消"其状态.使用名为undo_token的接口处理撤消,如下所示:
class undo_token
{
public:
void undo(my_struct* s) = 0;
};
Run Code Online (Sandbox Code Playgroud)
因此,由于多态性,我无法将指针传递给undo方法(mystruct也包含其他类型的变量).
当我向结构中添加一个新变量时,我通常还会添加一个新类,如下所示:
class undo_a : public undo_token
{
int new_value;
public:
undo_a(int new_value) { …Run Code Online (Sandbox Code Playgroud) 这可能最好用示例代码显示.以下无法使用g ++编译:
struct Base {
};
struct Derived : public Base {
};
struct Container {
Derived data_;
};
int main(void) {
Base Container::*ptr = &Container::data_;
}
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:invalid conversion from 'Derived Container::*' to Base Container::*'.语言不允许这样做吗?这是编译器错误吗?我使用了错误的语法吗?
请帮忙!
关于为什么我要这样做的一些背景:我有几个我想主要用作派生类型的成员数据,但我希望能够通过一些常用代码填充它们.数据将以任意顺序出现,并有一个字符串标签,我将用它来选择要填充的相应成员数据.我计划创建一个std::map<std::string, Base Container::*>通过公共接口为每个成员分配数据.我想避免使用巨型if else构造来找到正确的成员数据.
我正在努力了解它的实现std::is_class.我复制了一些可能的实现并编译它们,希望弄清楚它们是如何工作的.完成后,我发现所有的计算都是在编译期间完成的(因为我应该早点想出来,回头看看),所以gdb可以不再详细介绍究竟发生了什么.
我正在努力理解的实现是这样的:
template<class T, T v>
struct integral_constant{
static constexpr T value = v;
typedef T value_type;
typedef integral_constant type;
constexpr operator value_type() const noexcept {
return value;
}
};
namespace detail {
template <class T> char test(int T::*); //this line
struct two{
char c[2];
};
template <class T> two test(...); //this line
}
//Not concerned about the is_union<T> implementation right now
template <class T>
struct is_class : std::integral_constant<bool, sizeof(detail::test<T>(0))==1
&& !std::is_union<T>::value> {};
Run Code Online (Sandbox Code Playgroud)
我在使用两条注释行时遇到了麻烦.第一行:
template<class T> char test(int …Run Code Online (Sandbox Code Playgroud) 我在互联网上读到了这个偏移宏,但它没有解释它的用途.
#define offsetof(a,b) ((int)(&(((a*)(0))->b)))
Run Code Online (Sandbox Code Playgroud)
它想做什么以及使用它有什么好处?
是什么
private:
BOOL (LASreader::*read_simple)();
Run Code Online (Sandbox Code Playgroud)
意思?
它来自LAStools,位于lasreader.hpp
BOOL是一个typedef bool(来自mydefs.hpp),但我不知道这行是什么声明,特别是::*(双冒号星号),它看起来像一个函数调用.
#include <iostream>
class A {
protected:
void foo()
{}
};
class B : public A {
public:
void bar()
{
std::cout << (&A::foo) << std::endl;
}
};
int main()
{
B b;
b.bar();
}
Run Code Online (Sandbox Code Playgroud)
这里我试图获取基类的受保护成员函数的地址.我收到了这个错误.
main.cpp: In member function ‘void B::bar()’:
main.cpp:5: error: ‘void A::foo()’ is protected
main.cpp:13: error: within this context
make: *** [all] Error 1
Run Code Online (Sandbox Code Playgroud)
将foo改为公共工程.还有印刷&B::foo作品.能否解释为什么我们无法获得基类的受保护成员函数的地址?
我最近发现C++中存在.*运算符(以及密切相关的->*运算符).(见这个问题.)
起初看起来很整洁,但为什么我会需要这样的东西呢?链接问题中的两个答案提供了可以从直接函数调用中受益的人为例子.
在直接函数调用不方便的情况下,可以使用函数对象,就像可以使用的lambda函数一样std::sort.这消除了间接级别,因此比使用更高效.*.
链接的问题还提到了此示例的简化版本:
struct A {
int a;
int b;
};
void set_member(A& obj, int A::* ptr, int val){
obj.*ptr = val;
}
int main()
{
A obj;
set_member(obj, &A::b, 5);
set_member(obj, &A::a, 7);
// Both members of obj are now assigned
}
Run Code Online (Sandbox Code Playgroud)
但这样做是非常微不足道的(可能更好的做法,因为它更清洁,并且不会对成员造成不必要的限制A):
struct A {
int a;
int b;
};
void set_me(int& out, int val){
out = val;
}
int main()
{
A obj;
set_me(obj.b, …Run Code Online (Sandbox Code Playgroud)