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

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

本文关键字:实现 always false 标准 C++ 是否      更新时间:2023-10-16

在某些情况下,如果尝试实例化某个模板,则使用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:程序格式错误,无需诊断,如果:

  • 无法为模板生成有效的专门化[…],并且模板未实例化,或者

[…]

(因此,在上面写入static_assert(false, "You should not use this!");将是格式错误的,编译器总是可以激发静态断言,即使没有实例化模板,这也不是目的。)

以下是涉及这种模式的问题的快速抽样(包括进一步的解释):

  • 禁止使用"static_assert"的函数

  • 如果成员模板不是';t实例化?

  • 模板的条件编译

always_false作为标准库中的工具可能会很有用,这样我们就不必不断地重新编写它。然而,以下问题的答案让我怀疑这是否可能:

依赖的非类型参数包:标准怎么说?

这里提出了一个论点(也关于[temp.res]/8),即std::enable_if_t<T>总是void或不是类型,任何人进一步专门化它都是非法的。因此,一个模板依赖于理论上的";特殊性";为了避免[temp.res]/8子句,std::enable_if实际上导致程序格式错误,不需要诊断。

回到我的问题:如果标准提供always_false,它将不得不禁止图书馆用户像往常一样专门化它(原因很明显)。但根据上述推理,这将推翻always_false的全部观点(即理论上它可以专门用于std::false_type以外的东西)——关于[temp.res]/8,它将与直接使用std::false_type相同。

我的推理错了吗或者标准库实际上不可能以有意义/有用的方式(在没有核心语言更改的情况下)提供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{};

经过反思,我认为这是不可能的:封闭模板可能有其他特殊化的假设类型应该满足的限制:

对于static_assert(is_enum_v<T> && always_false_v<T>),该类型应该是枚举。

更受约束的是,对于static_assert(is_same_v<T, int> && always_false_v<T>),它适用于int

编辑:C++23现在允许在非实例化部分中使用static_assert(false);:)

套用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和专业化中的lambdas的问题。

所有这样的尝试都会导致程序格式错误,不需要诊断。

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

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

(写入static_assert(false,"你不应该使用这个!");因此,上面的格式不正确,编译器可能总是触发静态断言,即使没有实例化模板,这也不是目的。)

完全相同的结论适用于您的

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++吃水深度/温度res#6.1

程序格式错误,不需要诊断,如果:(6.1)不能为模板[…]"生成有效的专门化;

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

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

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病例的工作方式一致。

CCD_ 28旁路需要魔术;欺骗;编译器绕过标准中明确编写的部分,以阻止你做你想做的事情。

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

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