Clang 声称这个(部分专门化的)模板实例化是不明确的。这样对吗?

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 是对还是错呢?如果是对的,我还缺少什么?

Nel*_*eal 1

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 可以编译。

  1. 在两个专业化中指定参数Holderdemo):
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)
  1. 在两个专业化中指定参数Tdemo):
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)
  1. 在两个专业化中指定参数Cstdemo):
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{}

另请注意,在您的原始代码中,替换Holderstd::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)

演示