Подтвердить что ты не робот

Является ли declval <T>() тем же, что и (* (T *) nullptr)?

Является ли declval<T>() заменой старого трюка (*(T*)NULL), чтобы получить экземпляр 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 является более чем заменой, каковы различия и где это полезно?

4b9b3361

Ответ 1

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 предоставит тип rvalue, где nullptr дает lvalue.

Ответ 2

Нет, 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 (в С++ 14 это значение xvalue. Некоторые компиляторы, вероятно, будут рассматривать его как значение x даже в режиме С++ 11), E2 - это значение xvalue. E3 является lvalue и E4 снова является значением x.

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

В этих двух типах первый тип decltype дает тип члена, называемого выражением. Элемент имеет тип A, поэтому T1 - A. Второй тип decltype дает тип выражения, измененный &, если выражение является lvalue и изменено &&, если выражение является значением x. Выражение представляет собой lvalue, поэтому T2 - A&.