为什么C++20的requires表达式不按预期工作?

12
#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表达式没有按预期的方式工作?
1个回答

20
这是一个错误的期望。首先,类模板在翻译单元中只有一个实例化点:
[temp.point] 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.
}

[temp.names]

8 …如果指定的模板参数满足概念的标准化约束表达式,则概念ID为真([temp.constr.constr]),否则为假。

[temp.constr.atomic]

3 …如果在程序的不同点,对于相同的原子约束和模板参数,满足结果不同,则程序无效,无需诊断。

这并不是什么新鲜事物,概念只是增加了更多类似的内容。对于一组特定的参数,如果在程序的两个不同点中参数的某些属性不同,则模板的意义不得改变。

因此,尽管可以编写检查类型是否完整的概念(即使在C++20之前的SFINAE hackery中也可以),但粗心使用它就像玩火一样。


这甚至可以在实际操作中看到:https://cppinsights.io/s/67df151f 仅为 A 创建了1个新的 struct,并在两种情况下使用。 - mediocrevegetable1
你甚至可以通过向模板添加一个虚拟的、唯一的参数来“修复”它,例如 template<typename T, int _line>IsComplete<A, __LINE__> - Marandil
1
如果在不同的块中声明了struct A的两个声明,我们将得到两种不同的类型,并且两个断言都会通过:https://cppinsights.io/s/f7e69e33 - md2perpe
3
在这种情况下,@md2perpe,这是因为每个“结构体A”都定义了一个独特的类型,因此IsComplete会被实例化两次。链接 - Justin Time - Reinstate Monica
@JustinTime-ReinstateMonica。我知道。那就是我想要展示的内容。 - md2perpe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接