Looking at libstdc++ source code, I found the following declval
implementation:
template<typename _Tp, typename _Up = _Tp&&>
_Up __declval(int); // (1)
template<typename _Tp>
_Tp __declval(long); // (2)
template<typename _Tp>
auto declval() noexcept -> decltype(__declval<_Tp>(0));
This implementation was proposed by Eric Niebler as a compile time optimization: he explains that overload resolution is faster than template instantiation.
However, I can't understand how it works. Specifically:
_Up
better than just returning _Tp&&
? How all this prevents template instantiations, as opposed to the most naive implementation:
template<typename T>
T&& declval() noexcept;
The naive implementation is not fully correct. According to the Standard, declval
is defined as ([declval]):
template <class T> add_rvalue_reference_t<T> declval() noexcept;
and for add_rvalue_reference<T>
the Standard reads ([meta.trans.ref]):
If
T
names a referenceable type then the member typedeftype
namesT&&
; otherwise,type
namesT
.
An example of a non-referenceable type is void
. The second overload will be used in that case thanks to SFINAE.
As to the first question, I don't see any special reason. _Tp&&
should work just fine.
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