C++20 NTTP 专业化
C++20 NTTP specialization
尝试编译以下代码时,gcc/clang 和 msvc 之间存在分歧:
struct foo {
};
// primary template
template<auto>
struct nttp {
static constexpr int specializaion = 0;
};
// specialization
template<foo f>
struct nttp<f> {
static constexpr int specializaion = 1;
};
int main() {
// Does not compile with msvc 19.30
nttp<5> x{};
}
完整示例 here.
Msvc 抱怨没有从 int
到 foo
的可行转换,这显然是正确的,但据我了解部分模板的规则,这里不应该有任何相关性专业化。
引用 cppreference:
When a class or variable (since C++14) template is instantiated, and there are partial specializations available, the compiler has to decide if the primary template is going to be used or one of its partial specializations.
- If only one specialization matches the template arguments, that specialization is used
- If more than one specialization matches, partial order rules are used to determine which specialization is more specialized. The most specialized specialization is used, if it is unique (if it is not unique, the program cannot be compiled)
- If no specializations match, the primary template is used
我认为在这种情况下 int
没有匹配的专业化,因此应该选择主模板。
有趣的是,这种情况可以通过用一个概念约束专业化中的 auto
来解决:
template<class T>
concept Foo = std::is_same_v<T, foo>;
template<auto>
struct nttp {
static constexpr int specializaion = 0;
};
template<Foo auto f>
struct nttp<f> {
static constexpr int specializaion = 2;
};
// compiles with all compilers
static_assert(nttp<5>{}.specializaion == 0);
函数模板也按预期工作:
constexpr int test(auto) {
return 0;
}
constexpr int test(foo) {
return 1;
}
// compiles with all compilers
static_assert(test(5) == 0);
长话短说:根据经验和直觉,我会说这是 MSVC 中的错误,但一如既往,这里有可能涉及 UB。所以问题是:clang/gcc 正确,还是 MSVC,甚至全部正确?
这是打开的 MSVC 错误报告:
您的程序是 well-formed 根据 [temp.class.spec.match]/2 and [temp.class.spec.match]/3:
/2 A partial specialization matches a given actual template argument
list if the template arguments of the partial specialization can be
deduced from the actual template argument list, and the deduced
template arguments satisfy the associated constraints of the partial
specialization, if any.
/3 If the template arguments of a partial specialization cannot be
deduced because of the structure of its template-parameter-list and
the template-id, the program is ill-formed.
/3 作为 P0127R2 (Declaring non-type template parameters with auto
), which re-wrote the previously revised wording from the resolution of CWG1315
的一部分进行了专门更新
(After CWG1315, before P0123R2) /3 Each template-parameter shall appear at least once in the template-id outside a non-deduced context.
这 re-write 是专门为了允许对使用 auto
占位符类型声明的 non-template 参数进行部分特化。
尝试编译以下代码时,gcc/clang 和 msvc 之间存在分歧:
struct foo {
};
// primary template
template<auto>
struct nttp {
static constexpr int specializaion = 0;
};
// specialization
template<foo f>
struct nttp<f> {
static constexpr int specializaion = 1;
};
int main() {
// Does not compile with msvc 19.30
nttp<5> x{};
}
完整示例 here.
Msvc 抱怨没有从 int
到 foo
的可行转换,这显然是正确的,但据我了解部分模板的规则,这里不应该有任何相关性专业化。
引用 cppreference:
When a class or variable (since C++14) template is instantiated, and there are partial specializations available, the compiler has to decide if the primary template is going to be used or one of its partial specializations.
- If only one specialization matches the template arguments, that specialization is used
- If more than one specialization matches, partial order rules are used to determine which specialization is more specialized. The most specialized specialization is used, if it is unique (if it is not unique, the program cannot be compiled)
- If no specializations match, the primary template is used
我认为在这种情况下 int
没有匹配的专业化,因此应该选择主模板。
有趣的是,这种情况可以通过用一个概念约束专业化中的 auto
来解决:
template<class T>
concept Foo = std::is_same_v<T, foo>;
template<auto>
struct nttp {
static constexpr int specializaion = 0;
};
template<Foo auto f>
struct nttp<f> {
static constexpr int specializaion = 2;
};
// compiles with all compilers
static_assert(nttp<5>{}.specializaion == 0);
函数模板也按预期工作:
constexpr int test(auto) {
return 0;
}
constexpr int test(foo) {
return 1;
}
// compiles with all compilers
static_assert(test(5) == 0);
长话短说:根据经验和直觉,我会说这是 MSVC 中的错误,但一如既往,这里有可能涉及 UB。所以问题是:clang/gcc 正确,还是 MSVC,甚至全部正确?
这是打开的 MSVC 错误报告:
您的程序是 well-formed 根据 [temp.class.spec.match]/2 and [temp.class.spec.match]/3:
/2 A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list, and the deduced template arguments satisfy the associated constraints of the partial specialization, if any.
/3 If the template arguments of a partial specialization cannot be deduced because of the structure of its template-parameter-list and the template-id, the program is ill-formed.
/3 作为 P0127R2 (Declaring non-type template parameters with auto
), which re-wrote the previously revised wording from the resolution of CWG1315
(After CWG1315, before P0123R2) /3 Each template-parameter shall appear at least once in the template-id outside a non-deduced context.
这 re-write 是专门为了允许对使用 auto
占位符类型声明的 non-template 参数进行部分特化。