Понимание declval оптимизированной реализации
Глядя на исходный код libstdC++, я нашел следующее declval
реализация:
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));
Эту реализацию предложил Эрик Ниблер в качестве оптимизации времени компиляции: он объясняет, что разрешение перегрузки быстрее, чем создание шаблона.
Однако я не могу понять, как это работает. В частности:
- В (1), почему используется
_Up
лучше, чем просто вернуться_Tp&&
? - Кажется, что перегрузка (2) никогда не используется. Зачем это нужно?
Как все это предотвращает создание шаблонов, в отличие от самой наивной реализации:
template<typename T>
T&& declval() noexcept;
1 ответ
Наивная реализация не совсем верна. Согласно стандарту, declval
определяется как ([declval]):
template <class T> add_rvalue_reference_t<T> declval() noexcept;
и для add_rvalue_reference<T>
Стандарт гласит ([meta.trans.ref]):
Если
T
называет ссылочный тип, а затем член typedeftype
именаT&&
; иначе,type
именаT
,
Примером не ссылочного типа является void
, Вторая перегрузка будет использоваться в этом случае благодаря SFINAE.
Что касается первого вопроса, я не вижу особой причины. _Tp&&
должно работать просто отлично.