【问题标题】:Is declval<T>() the same as (*(T*)nullptr)?declval<T>() 是否与 (*(T*)nullptr) 相同?
【发布时间】:2013-01-17 09:39:37
【问题描述】:

declval&lt;T&gt;() 是否只是替代 (*(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 不仅仅是替代品,有什么区别,它在哪里有用?

【问题讨论】:

  • T 是引用类型时,你的老把戏还有效吗?

标签: c++ c++11 decltype


【解决方案1】:

declval() 的优点是,如果在评估的上下文中使用它(即 odr-used),则程序格式错误(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 给出一个左值。

【讨论】:

  • 只是为了确保我理解你的第一点:在 decltype 之外使用 declval 会导致编译错误,在同一个地方使用 (*(T*)NULL) 不会在编译时引起任何投诉和运行时的段错误。 decltype 显然更好。
  • @walrii 没错,但不能保证运行时的段错误。
  • 请注意,这并不总是严格意义上的优势;例如,如果出于某种原因您需要知道类成员的地址偏移量,the casting-a-literal trick will allow you to determine this without constructing an actual class object,但 declval 不会。
  • @ecatmur 据我所知,没有标准的方法来获取非 POD 或非标准布局类型的偏移量,这可能就是 AOSP 使用他们所做的代码的原因,即使它是技术上是 UB。
  • 有没有比“错误:静态断言失败:不能使用declval()!”更令人困惑的消息?一般的程序员会抱怨“如果我不能使用它为什么会存在”。它们可能意味着“不得用于 odr-used”,或者应该完全改变 IMO 的信息。
【解决方案2】:

不,declval&lt;T&gt;()(*(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 是一个纯右值(在 C++14 中它是一个 xvalue。即使在 C++11 模式下,一些编译器也可能会将其视为一个 xvalue),E2 是一个 xvalue。 E3 是一个左值,E4 又是一个 xvalue。

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

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

【讨论】:

  • 感谢您详细说明为什么B().a 是prvalue 我认为它是一个xvalue,我还认为E2 应该是declval&lt;B&gt;().a
  • @FallingFromBed B().a 是一个 xvalue,因为只有 c++14。我会添加注释
  • Johannes Schaub - litb ,非常感谢!
猜你喜欢
  • 1970-01-01
  • 2016-06-02
  • 1970-01-01
  • 2020-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多