Является ли 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 является более чем заменой, каковы различия и где это полезно?
Ответы
Ответ 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&
.