Можно ли реализовать Always_false в стандартной библиотеке C++?
Есть случаи, когда кто-то использует always_false
помощник, например, вызвать безусловную ошибку static_assert
при попытке создания экземпляра какого-либо шаблона:
template <class... T> struct always_false : std::false_type {};
template<class T>
struct UsingThisShouldBeAnError {
static_assert(always_false<T>::value, "You should not use this!");
};
Этот помощник необходим, потому что определение шаблона должно (хотя бы теоретически) иметь хотя бы один набор параметров шаблона, для которого может быть сформирована действительная специализация, чтобы программа была правильно сформирована:
[temp.res]/8: The program is ill-formed, no diagnostic required, if:
- Для шаблона [...] не может быть сгенерировано никакой действительной специализации, а шаблон не создан, или
[...]
(Таким образом, запись static_assert(false, "You should not use this!");
выше будет некорректной, и компилятор всегда сможет запустить статическое утверждение, даже без создания шаблона, что не является намерением.)
Вот краткий перечень вопросов, связанных с этим шаблоном (включая дальнейшие пояснения):
Может быть полезно иметь always_false
в качестве инструмента в стандартной библиотеке, поэтому нам не нужно постоянно писать его снова. Однако ответ на следующий вопрос заставляет меня задуматься, возможно ли это вообще:
Зависимые нетиповые пакеты параметров: что говорит стандарт?
Там делается аргумент (также в отношении [temp.res]/8), что std::enable_if_t<T>
всегда является либо void
, либо не типом, и что для любого специалиста его дальнейшая специализация запрещена. Следовательно, шаблон, опирающийся на теоретическую "специализацию" std::enable_if
, чтобы избежать предложения [temp.res]/8, фактически приводит к некорректной работе программы, диагностика не требуется.
Возвращаясь к моему вопросу: если бы стандарт предусматривал always_false
, он должен был бы запретить пользователям библиотеки специализировать его как обычно (по очевидным причинам). Но, исходя из приведенных выше рассуждений, это противоречило бы всему смыслу always_false
(а именно, что оно теоретически может быть специализировано для чего-то иного, чем std::false_type
) - в отношении [temp.res]/8 это будет то же самое, что и использование std::false_type
напрямую.
Я ошибаюсь в этих рассуждениях? Или стандартная библиотека фактически не может предоставить always_false
значимым/полезным способом (без изменений основного языка)?
Ответы
Ответ 1
Перефразируя идею Джарода, это может быть что-то вроде
template <class... T> struct always_false : std::false_type {};
template <> struct always_false</* implementation defined */> : std::true_type{};
Где /* implementation defined */
может быть заполнено std::_ReservedIdentifer
. Пользовательский код не может получить к нему доступ, поскольку идентификатор зарезервирован для библиотеки, но существует специализация true
. Это должно избежать вопросов о ODR и лямбдах в специализациях.
Ответ 2
В С++ 20 с лямбдой вы можете сделать что-то вроде:
template <class... T> struct always_false : std::false_type {};
// To have true, but for a type that user code can't reuse as lambda types are unique.
template <> struct always_false<decltype([](){})> : std::true_type{};