模糊过载 - 使用参数包进行部分功能模板排序

Bar*_*rry 10 c++ gcc templates clang language-lawyer

考虑以下设计的代码:

template <class... > struct pack { };

template <class R, class T, class... Args>
int foo(pack<T>, Args...)
{
    return sizeof(R);
}

template <class R, class T, class... Ts, class... Args>
int foo(pack<T, Ts...>, Args... args)
{
    return foo<T>(pack<Ts...>{}, args...);
}

int main() {
    // gcc: OK, clang: ambiguous
    foo<int>(pack<int>{});

    // gcc: ambiguous, clang: ambiguous
    foo<int>(pack<int>{}, 0);
}
Run Code Online (Sandbox Code Playgroud)

如果第二个重载更改为包含至少2种类型而不是至少一种类型的包,则gcc和clang都接受两个调用:

template <class R, class T, class T2, class... Ts, class... Args>
int foo(pack<T, T2, Ts...>, Args... args)
{
    return foo<T>(pack<T2, Ts...>{}, args...);
}
Run Code Online (Sandbox Code Playgroud)

如果将非推导模板参数移动到推导出的模板参数,则:

template <class... > struct pack { };

template <class R, class T, class... Args>
int foo(pack<R>, pack<T>, Args...)
{
    return sizeof(R);
}

template <class R, class T, class... Ts, class... Args>
int foo(pack<R>, pack<T, Ts...>, Args... args)
{
    return foo(pack<T>{}, pack<Ts...>{}, args...);
}

int main() {
    // gcc ok with both, clang rejects both as ambiguous
    foo(pack<int>{}, pack<int>{});
    foo(pack<int>{}, pack<int>{}, 0);
}
Run Code Online (Sandbox Code Playgroud)

我希望在每个版本的调用都可以.上述代码示例的预期结果是什么?

Wal*_*ter -1

我们先把问题简单化考虑一下

template <class...> struct pack {};

template <class T>
void foo(pack<T>) {}

template <class T, class... Ts>
void foo(pack<T,Ts...>) {}

int main() {
  foo(pack<int>{});
}
Run Code Online (Sandbox Code Playgroud)

clang 抱怨并拒绝编译,声称void foo(pack<T>)[with T=int] 和void foo(pack<T,Ts...>)[with T=int, Ts=<>] 之间存在歧义。像这样的情况可以使用重载函数模板的部分排序来解决,它本质上是试图找到最专门的重载。

就目前的情况而言,我认为以下内容适用:

在平局的情况下,如果一个函数模板具有尾随参数包,而另一个函数模板没有,则具有省略参数的函数模板被认为比具有空参数包的函数模板更专业。

因此看来第一个应该是首选,而 clang 是错误的。