Wan*_*nze 5 c++ language-lawyer c++20
#include <concepts>
class Base {};
class Derived : public Base {};
// primary template
template <typename T>
requires std::derived_from<T, Base>
struct C {
static constexpr int value = 1;
};
// specialization
template <typename T>
requires std::derived_from<T, Derived>
struct C<T> {
static constexpr int value = 2;
};
static_assert(C<Derived>::value == 2);
Run Code Online (Sandbox Code Playgroud)
事实证明,MSVC 可以很好地编译代码 ( godbolt ),而 GCC 拒绝编译并显示错误消息 ( godbolt ):
错误:部分特化“struct C”不特化任何模板参数,并且不比主模板受到更多约束
我想知道根据语言标准,哪种行为是正确的,以及专门针对C++20 中C<T>所有T派生自Derived(而不是Base)(包括自身)的正确方法是什么。Derived
海湾合作委员会是正确的。
std::derived_from<T, Derived>不包含 std::derived_from<T, Base>,因此部分特化并不比主模板更特化,这是不允许的。
这是从[temp.spec.partial.general]/9.2到[temp.spec.partial.order]、[temp.func.order]/6.4、[temp.constr.order]/1.2以及最后[temp.constr .原子]/2。
std::derived_from<T, X>包含约束的合取,其中一个出于示例目的而选择的为std::is_base_of_v<X, T>。
std::is_base_of_v不是一个概念,而是一个变量模板。因此,主模板具有原子约束std::is_base_of_v<X, Base>,而专业化则具有原子约束std::is_base_of_v<X, Derived>。这两个原子约束并不等同。因此,在模板的两个约束中,没有一个比另一个更专业。
为了确保部分专业化的约束包含主要专业化的约束,您需要
template <typename T>
requires std::derived_from<T, Base> && std::derived_from<T, Derived>
struct C<T> { //...
Run Code Online (Sandbox Code Playgroud)
现在这些约束包含了主模板上的约束。
另请注意,这不仅仅是如何实现约束/概念的问题:
std::derived_from<T, Derived>并且Derived源自Base并不意味着std::derived_from<T, Base>.
std::derived_from要求基础是明确的并且是可访问的。最后一部分与 不同std::is_base_of。
例子:
struct A {};
struct B : A {};
struct C : A {};
struct D : B, C {};
static_assert(std::derived_from<B, A>);
static_assert(std::derived_from<D, B>);
static_assert(!std::derived_from<D, A>);
Run Code Online (Sandbox Code Playgroud)
另一个:
struct A {};
struct B : private A {};
struct D : B {};
static_assert(std::derived_from<B, A>);
static_assert(std::is_base_of<B, D>);
static_assert(!std::derived_from<D, A>);
Run Code Online (Sandbox Code Playgroud)