declval <t>()与(*(T *)nullptr)相同吗?</t>

时间:2013-02-02 00:01:11

标签: c++ c++11 decltype

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不仅仅是替代品,那么差异在哪里以及它在哪里有用?

2 个答案:

答案 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个表达式中,所有表达式都具有类型AE1是一个prvalue(在C ++ 14中它是一个xvalue。有些编译器可能会将它视为xvalue,即使在它的C ++ 11模式下),E2是一个xvalue。 E3是左值,E4再次是xvalue。

// T1: decltype((*(B*)0).a)
// T2: decltype(((*(B*)0).a))

在这两种类型中,第一个decltype给出了由表达式命名的成员的类型。该成员的类型为A,因此T1A。第二个decltype生成表达式的类型,如果表达式是左值,则由&修改,如果表达式是xvalue,则由&&修改。表达式是左值,因此T2A&