首页 > 解决方案 > 为什么 C++20 的 requires 表达式不符合预期?

问题描述

#include <type_traits>

template<typename T>
struct IsComplete final
    : std::bool_constant<requires{sizeof(T);}>
{};

int main()
{
    struct A;
    static_assert(!IsComplete<A>::value); // ok

    struct A{};
    static_assert(IsComplete<A>::value);  // error
}

我预计第二个static_assert应该是真的,因为 A 现在是一个完整的类型。

为什么 C++20 的 requires 表达式不符合预期?

标签: c++templatesc++20sfinaetypetraits

解决方案


这是一个错误的期望。首先,类模板在翻译单元中只有一个实例化点:

[温度点]

7 ...类模板的特化在翻译单元内最多有一个实例化点。任何模板的特化都可能在多个翻译单元中具有实例化点。如果根据单一定义规则,两个不同的实例化点赋予模板特化不同的含义,则程序是非良构的,不需要诊断。

模板从不允许程序中的两个点对同一组参数的模板有不同的解释(一般情况下的 ODR 噩梦)。您基本上是通过尝试该特征开始冒险进入鼻恶魔领域。

如果你认为使用 C++20 概念会改变任何东西,你就会陷入格式错误的问题;如果您对示例进行概念化,则无需诊断区域

template<typename T>
concept IsComplete = requires{sizeof(T);};

int main()
{
    struct A;
    static_assert(!IsComplete<A>); // ok

    struct A{};
    static_assert(IsComplete<A>);  // error or nuclear launch.
}

[临时名称]

8 ... 如果指定的模板参数满足概念的规范化约束表达式 ([temp.constr.constr]),则概念 ID 评估为 true,否则为 false。

[temp.constr.atomic]

3 ...如果在程序的不同点,相同的原子约束和模板参数的满足结果不同,则程序格式错误,不需要诊断。

这不是什么新东西,概念只是增加了更多相同的东西。如果参数的某些属性在程序中的两个不同点不同,则模板对特定参数集的含义不得改变。

因此,虽然可以编写一个检查类型是否完整的概念(即使在 C++20 之前的 SFINAE 黑客中),但粗心地使用它就是在玩火。


推荐阅读