不完整-class 上下文中的 C++ 概念检查

C++ concept checking in incomplete-class context

请考虑一个C++20概念程序:

struct A {};

template<typename T>
concept DerivedOnceFromA = requires(T t) { { static_cast<const A&>(t) }; };

template<DerivedOnceFromA T>
struct B {};

struct C : A
{
    B<C> foo();
};

这里的概念 DerivedOnceFromA 检查 T 是否可以静态转换为 AB 是遵循这个概念的模板结构。而 struct C 是从 A 派生出来的(所以这个概念是满足的)并且定义了一个 returns B<C>.

的函数

此代码被 GCC 和 MSVC 接受,但被 Clang 拒绝并出现错误:

constraints not satisfied for class template 'B' [with T = C]

演示:https://gcc.godbolt.org/z/7Tc7xdbeq

在 class 体内使用 class 作为概念模板参数是否合法(那么哪个编译器是正确的)?

合法吗?是的,它在范围内。但是您无法从中收集到太多有用的信息。

[class.mem.general]

6 A complete-class context of a class is a

  • function body ([dcl.fct.def.general]),
  • default argument,
  • noexcept-specifier ([except.spec]), or
  • default member initializer within the member-specification of the class.

7 A class is considered a completely-defined object type ([basic.types]) (or complete type) at the closing } of the class-specifier. The class is regarded as complete within its complete-class contexts; otherwise it is regarded as incomplete within its own class member-specification.

所以你在使用它的时候它仍然被认为是不完整的,最多只是你转发声明的一种类型。因此,无论启用转换的机制(继承或用户定义的转换运算符)如何,您都不知道它是否可以转换为其他引用。即使借助像 std::derived_from 这样的标准概念,也不可能知道一个不完整的类型是否派生自 A。不完整类型的可观察属性非常有限。

在 class 的正文中,class 的类型不完整。

因此您不能拥有依赖于类型完整的成员签名。

为了让您的代码正常工作,您需要将概念检查推迟到 class 完成之后。一种方法是通过模板方法:

template<std::same_as<C> Y=C>
B<Y> foo()
{
    return {};
}

您甚至可以在 cpp 文件中实现 Y=C 的唯一有效专业化。

这是一个有点愚蠢的解决方法,它确实会阻止虚拟方法。