是否可以在C++标准库中实现always_false?

Is it possible to implement always_false in the C++ standard library?

在某些情况下,人们会使用 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:

  • no valid specialization can be generated for a template [...] and the template is not instantiated, or

[...]

(因此,上面的 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 相同。

我这个推理错了吗? 或者标准库实际上不可能以 meaningful/useful 的方式提供 always_false(没有核心语言变化)?

在 C++20 中,使用 lambda,您可以执行如下操作:

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{};

套用 Jarod 的想法,可能是这样的

template <class... T> struct always_false : std::false_type {};

template <> struct always_false</* implementation defined */> : std::true_type{};

其中/* implementation defined */可以填std::_ReservedIdentifer。用户代码无法访问它,因为标识符是为库保留的,但存在 true 的特化。这应该避免关于专业化中的 ODR 和 lambda 的问题。

只需使用选项 -fdelayed-template-parsing

所有此类尝试都会导致程序格式错误,无需诊断。

阻止您使用 static_assert(false) 的子句使您的程序格式错误,不需要根据进行实际实例化的实际可能性进行诊断,而不是基于编译器是否可以解决它。

这些技巧只会让编译器更难检测您的程序格式错误这一事实。他们发出的诊断 不是必需的 ,并且您绕过发出的诊断的能力仅意味着您使编译器生成格式错误的程序,标准对其行为没有任何限制。

(Writing static_assert(false, "You should not use this!"); above would thus be ill-formed and a compiler could always fire the static assert, even without the template being instantiated, which is not the intention.)

您的结论完全相同

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!");
};

我声称在上面的程序中没有 UsingThisShouldBeAnError 的有效实例化。

http://eel.is/c++draft/temp.res#6.1

The program is ill-formed, no diagnostic required, if: (6.1) no valid specialization can be generated for a template [...]"

无法为此模板生成有效的专业化。

要避免这个陷阱,您的程序必须具有

template <> struct always_false<SomeListOfTypes> : std::true_type {};

并且如果在标准中指定了 always_false 而这不会发生,则使用标准中的 always_false 对您根本没有帮助。 因为标准要求专业化“可以生成”。

如果实例化模板的唯一方法是在格式错误的程序中,那么这是对“可以”一词的巨大延伸。因此,使用您在 true_type 专业化中不允许使用的保留类型或魔法类型是不合理的。

退一步说,“格式不正确,ndr”的目的是因为标准编写者希望允许对损坏的代码进行诊断,但不想强制执行。检测此类损坏的代码通常是一个难以停止的问题,但可以检测到简单的情况。围绕代码未被破坏的假设进行优化可能很有用。

所有注入 static_assert(false, "message") 的尝试都是围绕 意图 进行的,即 C++ 代码旨在有效。

我们有一个函数构造,对于它来说,成功查找是语言中已经存在的错误。

template<class T>
void should_never_be_found(tag<T>) = delete;

漏洞当然是这里缺少诊断消息。

template<class T>
void should_never_be_found(tag<T>) = delete("Provide an custom ADL overload!");

此外,无法 =delete 模板专业化。

想要的是:

template<class T>
struct UsingThisShouldBeAnError = delete("You should not use this");

这是直接的、有意的,并且与其他 =delete 案例的工作方式一致。

static_assert 绕过需要魔法和“欺骗”编译器来绕过明确编写的标准部分,以防止您做您想做的事。

语言中缺少模板 类 上的 =delete("string")=delete 语法。