不完整-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
是否可以静态转换为 A
。 B
是遵循这个概念的模板结构。而 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
的唯一有效专业化。
这是一个有点愚蠢的解决方法,它确实会阻止虚拟方法。
请考虑一个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
是否可以静态转换为 A
。 B
是遵循这个概念的模板结构。而 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
的唯一有效专业化。
这是一个有点愚蠢的解决方法,它确实会阻止虚拟方法。