Можно ли реализовать 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{};