C++中,对类型进行操作的有 typedef,using,decltype。
decltype 不对表达式求值,这一点同sizeof 一样。所以如果表达式中包含函数调用,并不需要看到该函数的定义实现,只需看到声明即可。
struct Default {
int foo() const {return 1;}
};
struct NonDefault {
NonDefault(const NonDefault&) {}
int foo() const {return 1;}
};
int main()
{
decltype(Default().foo()) n1 = 1; // int n1
// decltype(NonDefault().foo()) n2 = n1; // error: no default constructor
decltype(std::declval<NonDefault>().foo()) n2 = n1; // int n2
std::cout << "n2 = " << n2 << '\n';
}
这里 NonDefault() 都不存在,所以虽然不发生调用的过程,仍然会有编译错误。这种情况还有构造函数被标记为 = delete,一样相当于不存在。
declval<T>() 也可以写成 declval(T) ,等价的。
declval<T>() 的类型 为 类型T加上右值引用,这里存在引用折叠,只有当T是左值引用类型时,返回的是左值引用,其他情况,返回的都是右值引用类型。
C++中的引用语义使得引用变量总是代表其所绑定的对象,通过类型转换,可以将引用变量绑定到对象地址开始的部分内存上。即将对象转换为引用类型,相当于为对象绑定了一个引用变量。
c++中的变量名本来就对应着存储该对象的一块内存(包括数组名),而不是现代编程语言中的名称与对象分离。将对象转化为引用就是将对象的起始地址开始的内存绑定到引用上。
int i = 0xeedd;
char& ref = reinterpret_cast<char&>(i);
ref = 0xff;
printf("%x", i);
输出:eeff . //这里是小端模式
需要说明的地方:C++11中存在左值和右值引用,对于上边的例子,reinterpret_cast<char&&>(i) 得到的是右值,reinterpret_cast<char&>(i)得到的却是左值,虽然得到的值都是匿名的对象。这也是为啥标准库中的 move() 函数其实进行的操作就是转换为右值引用。有名对象 ==》左值, 逆否命题: 右值==》匿名对象。补充的:匿名对象也可以是左值。
更细的值分类,可以看这里。
或者用指针的方式来表达绑定的过程:
int i = 0xeedd;
char& p = *(char*)&i;
p = 0xcc;
printf("%x", i);
或者用C语言的表达方式:
char* ptr = (char*)&i;
*ptr = 0xaa;
printf("%x", i);