I am reading a book, that explains C++ traits and there is an example from C++ type_traits header with a strange ?:
usage, here is the quote from the corresponding /usr/include/c++/... file:
template<typename _Tp, typename _Up>
static __success_type<typename decay<decltype
(true ? std::declval<_Tp>()
: std::declval<_Up>())>::type> _S_test(int);
Setting aside the purpose of the given declaration, the ?:
operator usage puzzles me in this code. If the first operand is true
, then std::declval<_Tp>()
will always be chosen as result of the evaluation.
How does that declval operand selection actually works?
Edit: originally read in Nicolai M. Josuttis's "The C++ Standard Library: A Tutorial and Reference, 2nd ed.", p.125. But there it is given in a slightly simplified form as compared to what my GCC header files has.
The decltype type specifier, together with the auto keyword, is useful primarily to developers who write template libraries. Use auto and decltype to declare a template function whose return type depends on the types of its template arguments.
Decltype keyword in C++ Decltype stands for declared type of an entity or the type of an expression. It lets you extract the type from the variable so decltype is sort of an operator that evaluates the type of passed expression. SYNTAX : decltype( expression )
decltype returns If what we pass to decltype is the name of a variable (e.g. decltype(x) above) or function or denotes a member of an object ( decltype x.i ), then the result is the type of whatever this refers to. As the example of decltype(y) above shows, this includes reference, const and volatile specifiers.
In the expression true ? std::declval<_Tp>() : std::declval<_Up>()
the first alternative is always selected, but the whole expression must be a valid expression. So std::declval<_Up>()
must be valid and that means _Up
must be a callable that accepts zero arguments. Beside that, _Tp()
and _Up()
must return the same type (or one of the types must be implicitly convertible to another), otherwise ternary iterator would not be able to select return value.
This technique is called SFINAE (substitution failure is not an error). The idea is that if template instantiation fails, then it is not an error, and this template is just ignored and compiler searches for another one.
The idea here is that ?:
requires that the second and third operand has the same type, or one type is convertible to the other.
Otherwise the instantiation of the function will fail, and some other overload is selected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With