declval<T>()
只是(*(T*)NULL)
的旧技巧的替代品,可以在decltype中获取T的实例而无需担心T的构造函数吗?
以下是一些示例代码:
struct A {};
struct B {
A a;
};
typedef decltype((*(B*)nullptr).a) T1;
typedef decltype(declval<B>().a) T2;
cout << "is_same: " << is_same<T1, T2>::value << endl;
打印1,因为T1和T2的类型相同。
如果declval不仅仅是替代品,那么差异在哪里以及它在哪里有用?
答案 0 :(得分:27)
declval()
的优势在于,如果在评估的上下文中使用它(即使用了odr),那么程序就会形成错误(20.2.4p2),并且需要发出诊断(每个1.4p1)。通常,这是通过库中的static_assert
强制执行的:
c++/4.7/type_traits: In instantiation of '[...] std::declval() [...]':
source.cpp:3:22: required from here
c++/4.7/type_traits:1776:7: error: static assertion failed: declval() must not be used!
declval
也适用于引用类型:
using S = int &;
using T = decltype(std::declval<S>());
using U = decltype(*(S *)nullptr); // fails
如果类型不是引用类型,declval
会给出一个右值类型,其中nullptr
给出一个左值。
答案 1 :(得分:7)
不,declval<T>()
与(*(T*)nullptr)
不同。 decltype(expr.bar)
与decltype((expr.bar))
不同。
前者比较表达式。后者使用decltype
检查表达式,decltype
的前一次使用检查expr.bar
的声明类型。所以你必须使用decltype
操作数来对这些类型进行有用的比较,你会发现它们是不同的。
struct A {};
struct B {
A a;
};
// E1: B().a
// E2: declval<A>().a
// E3: (*(B*)0).a
// E4: ((B&&)(*(B*)0)).a
在这4个表达式中,所有表达式都具有类型A
。 E1
是一个prvalue(在C ++ 14中它是一个xvalue。有些编译器可能会将它视为xvalue,即使在它的C ++ 11模式下),E2
是一个xvalue。 E3
是左值,E4
再次是xvalue。
// T1: decltype((*(B*)0).a)
// T2: decltype(((*(B*)0).a))
在这两种类型中,第一个decltype给出了由表达式命名的成员的类型。该成员的类型为A
,因此T1
为A
。第二个decltype生成表达式的类型,如果表达式是左值,则由&
修改,如果表达式是xvalue,则由&&
修改。表达式是左值,因此T2
为A&
。