Подтвердить что ты не робот

Разрешено ли использовать decltype для std:: declval <T> (сама функция, а не результат ее вызова)?

Следующий код запускает статическое утверждение на libstdС++:

#include <utility>

using t = decltype(std::declval<const void>);

Должен ли он?


Мотивация для этого вопроса:

Следующая реализация declval предложенная Эриком Ниблером (которая, по-видимому, является оптимизацией времени компиляции)

template<typename _Tp, typename _Up = _Tp&&>
_Up __declval(int);

template<typename _Tp>
_Tp __declval(long);

template<typename _Tp>
auto declval() noexcept -> decltype(__declval<_Tp>(0));

будет сомнительным, если пользователь может юридически наблюдать тип std::declval<const void>. Подпись в стандартном

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

приводит к типу const void () (или const void () noexcept в С++ 17), тогда как предлагаемая версия приводит к типу void () (или void () noexcept).

4b9b3361

Ответ 1

[declval] указывает, что:

Если эта функция используется odr (3.2), программа плохо сформирована.

Это в основном это. В случае использования функций odr-use из [basic.def.odr]:

Функция, имя которой отображается как потенциально оцененное выражение, используется как odr если это уникальный результат поиска или выбранный элемент набора перегруженных функций (3.4, 13.3, 13.4), если он не является чистой виртуальной функцией, и либо его имя явно не квалифицировано, либо формы выражения указатель на элемент (5.3.1).

Но также:

Выражение потенциально оценивается, если оно не является неоцененным операндом (п. 5) или подвыражением его.

И [dcl.type.simple]:

Операндом спецификатора decltype является неоцениваемый операнд (пункт 5).

Итак, в decltype(std::declval<const void>), std::declval не оценивается потенциально и, следовательно, не используется odr. Так как один критерий на declval для программы будет плохо сформирован, и мы не встречаем его, я думаю, что libstdС++ ошибочно испускает статическое утверждение.


Хотя я не думаю, что это вещь libstС++. Я думаю, что это больше вопрос о том, когда срабатывает static_assert. Реализация libstdС++ declval:

template<typename _Tp> 
struct __declval_protector
{    
    static const bool __stop = false;
    static typename add_rvalue_reference<_Tp>::type __delegate();
};   

template<typename _Tp> 
inline typename add_rvalue_reference<_Tp>::type
declval() noexcept
{    
    static_assert(__declval_protector<_Tp>::__stop,
         "declval() must not be used!");
    return __declval_protector<_Tp>::__delegate();
} 

Оба gcc и clang запускают в этом контексте static_assert (но, очевидно, не с decltype(std::declval<const void>()), хотя в обоих случаях мы находимся в неоценимом контексте. стандартное, какое правильное поведение имеет отношение к запуску static_assert s.