Can*_*nos 7 c++ clang template-specialization language-lawyer c++17
从 clang 9.0.0 开始,所有版本的 clang 在使用 -std=c++17 (或 -std=c++20)编译时都会拒绝以下代码:
#include <utility>
template<class L, std::size_t N>
struct A;
// #1
template
< template<class X, X...> class Holder
, class T, T... Cst
, std::size_t N
>
struct A<Holder<T, Cst...>, N>;
// #2
template
< template<class X, X...> class Holder
, class T, T... Cst
>
struct A<Holder<T, Cst...>, 0> {};
int main() {
A<std::index_sequence<>, 0> a{}; // supposed to select #2
}
Run Code Online (Sandbox Code Playgroud)
据说A<std::index_sequence<>, 0>是因为实例化不明确:
<source>:20:33: error: ambiguous partial specializations of 'A<std::integer_sequence<unsigned long>, 0>'
A<std::index_sequence<>, 0> a{};
^
<source>:11:8: note: partial specialization matches [with Holder = integer_sequence, T = unsigned long, Cst = <>, N = 0]
struct A<Holder<T, Cst...>, N>;
^
<source>:17:8: note: partial specialization matches [with Holder = integer_sequence, T = unsigned long, Cst = <>]
struct A<Holder<T, Cst...>, 0> {};
Run Code Online (Sandbox Code Playgroud)
使用 -std=c++14 编译不会出现错误。使用 gcc 或 clang 8.0.1(或更早版本)编译也不会。 神螺栓链接
我不明白为什么这会是模棱两可的。我希望Holder = Holder0, T=T0, Cst=Cst0, N=2在调用时能够正确推断出以下内容f:
<source>:20:33: error: ambiguous partial specializations of 'A<std::integer_sequence<unsigned long>, 0>'
A<std::index_sequence<>, 0> a{};
^
<source>:11:8: note: partial specialization matches [with Holder = integer_sequence, T = unsigned long, Cst = <>, N = 0]
struct A<Holder<T, Cst...>, N>;
^
<source>:17:8: note: partial specialization matches [with Holder = integer_sequence, T = unsigned long, Cst = <>]
struct A<Holder<T, Cst...>, 0> {};
Run Code Online (Sandbox Code Playgroud)
godbolt 链接
,而对于任意 N,相反的操作通常会失败:
template
< template<class X, X...> class Holder
, class T, T... Cst
, std::size_t N
>
void f(A<Holder<T, Cst...>, N>) {} // obtained from #1
template
< template<class X, X...> class Holder0
, class T0, T0... Cst0
>
struct Context {
static void g() {
f(A<Holder0<T0, Cst0...>, 0>{}); // obtained from #2
}
};
Run Code Online (Sandbox Code Playgroud)
godbolt 链接
根据我对 N4659(c++17 的最终工作草案)和 [temp.class.order]/1 的理解,这应该意味着 #2 比 #1 更专业。
话又说回来,我不擅长阅读标准,而 clang 只给我最新版本(编译器和 c++ 标准)的错误结果让我重新审视自己。
那么,这里 clang 是对还是错呢?如果是对的,我还缺少什么?
Clang在这里绝对是错误的。你的分析是正确的。
考虑以下代码片段,与您的代码片段类似,但没有任何参数包,并且使用函数模板而不是类模板专业化(在[temp.class.order]中解释了重写):
#include <utility>
template<class L, std::size_t N>
struct A {};
template<class T, T>
struct TestHolder {};
template<template<class X, X> class Holder, class T, T Cst, std::size_t N>
void f(A<Holder<T, Cst>, N>) {}
template<template<class X, X> class Holder, class T, T Cst>
void f(A<Holder<T, Cst>, 0>) {}
int main() {
f(A<TestHolder<int, 0>, 0>{});
}
Run Code Online (Sandbox Code Playgroud)
这展示了同样的问题(演示)。以下是一些可能的更改,这些更改不应影响专业化排序,但 clang 可以编译。
Holder(demo):template<class T, T Cst, std::size_t N>
void f(A<TestHolder<T, Cst>, N>) {}
template<class T, T Cst>
void f(A<TestHolder<T, Cst>, 0>) {}
Run Code Online (Sandbox Code Playgroud)
T(demo):template<template<class X, X> class Holder, int Cst, std::size_t N>
void f(A<Holder<int, Cst>, N>) {}
template<template<class X, X> class Holder, int Cst>
void f(A<Holder<int, Cst>, 0>) {}
Run Code Online (Sandbox Code Playgroud)
Cst(demo):template<template<class X, X> class Holder, class T, std::size_t N>
void f(A<Holder<T, T{}>, N>) {}
template<template<class X, X> class Holder, class T>
void f(A<Holder<T, T{}>, 0>) {}
Run Code Online (Sandbox Code Playgroud)
0由于某种原因,当使用代替时,MSVC 会被这个问题卡住T{}。
另请注意,在您的原始代码中,替换Holder为std::integer_sequence也有效:
#include <utility>
template<class, std::size_t N>
struct A;
template<class T, T... Cst, std::size_t N>
struct A<std::integer_sequence<T, Cst...>, N>;
template<class T, T... Cst>
struct A<std::integer_sequence<T, Cst...>, 0> {};
int main() {
A<std::index_sequence<>, 0> a{};
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
127 次 |
| 最近记录: |