Понимание declval оптимизированной реализации - PullRequest
14 голосов
/ 20 мая 2019

Глядя на исходный код 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. В (1) почему использование _Up лучше, чем просто возврат _Tp&&?
  2. Кажется, перегрузка (2) никогда не используется. Зачем это нужно?

Как все это предотвращает создание шаблонов, в отличие от самой наивной реализации:

template<typename T>
T&& declval() noexcept;

1 Ответ

12 голосов
/ 20 мая 2019

Наивная реализация не полностью корректна.Согласно Стандарту declval определяется как ([declval]):

template <class T> add_rvalue_reference_t<T> declval() noexcept;

, а для add_rvalue_reference<T> Стандарт гласит ([мета.trans.ref]):

Если T называет ссылочный тип, тогда член typedef type names T&&;в противном случае type names T.

Примером не ссылочного типа является void.Вторая перегрузка будет использоваться в этом случае благодаря SFINAE.

Что касается первого вопроса, я не вижу особой причины._Tp&& должно работать просто отлично.

...