是一个c ++ 11可变参数函数模板重载与依赖类型不明确?

Bob*_*tti 9 c++ gcc clang language-lawyer c++11

以下代码是递归可变参数函数重载的教科书示例.在clang和GCC中,它编译干净,并main返回36(如预期的那样):

template <typename T>
int add(T val)
{
    return val;
}

template <typename FirstTypeT, typename... RestT>
int add(FirstTypeT first_value, RestT... rest)
{
    return first_value + add<RestT...>(rest...);
}

int main(void)
{
    return add(12, 12, 12);
}
Run Code Online (Sandbox Code Playgroud)

但是,这里稍作修改.它直接在模板定义中使用依赖类型而不是模板参数:

struct Foo
{
    using SomeType = int;
};

template <typename T>
int add(typename T::SomeType val)
{
    return val;
}

template <typename FirstT, typename... RestT>
int add(typename FirstT::SomeType first_value, typename RestT::SomeType... rest)
{
    return first_value + add<RestT...>(rest...);
}

int main(void)
{
    return add<Foo, Foo, Foo>(12, 12, 12);
}
Run Code Online (Sandbox Code Playgroud)

它编译和运行为使用GCC 5.2预期,但未能使用铛3.8:

clang++ variadic.cpp -o var -std=c++11 -Wall
variadic.cpp:15:26: error: call to 'add' is ambiguous
    return first_value + add<RestT...>(rest...);
                         ^~~~~~~~~~~~~
variadic.cpp:15:26: note: in instantiation of function template specialization 'add<Foo, Foo>' requested here
    return first_value + add<RestT...>(rest...);
                         ^
variadic.cpp:20:12: note: in instantiation of function template specialization 'add<Foo, Foo, Foo>' requested here
    return add<Foo, Foo, Foo>(12, 12, 12);
           ^
variadic.cpp:7:5: note: candidate function [with T = Foo]
int add(typename T::SomeType val)
    ^
variadic.cpp:13:5: note: candidate function [with FirstT = Foo, RestT = <>]
int add(typename FirstT::SomeType first_value, typename RestT::SomeType... rest)
    ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

我的问题是双重的.

  1. 是否真的有效地使用参数包typename模式将范围解析运算符应用于包的每个成员typename RestT::SomeType...
  2. 铿锵对标准是否正确,或者这是一个错误?第二个例子真的比第一个更暧昧吗?(对于第一个例子,似乎你可以说单个参数重载与第二个实例化时不明确RestT = <>)

Col*_*mbo 7

  1. 是的,没关系.
  2. 目前的措辞非常清楚:在部分排序期间完全忽略参数包,因为它没有参数([temp.deduct.partial] /(3.1)).[temp.func.order]/5也提供了一个非常简单的例子,即使使用可推导的模板参数 - 表明你的第一个例子也是不明确的:

    [ 注意:由于调用上下文中的部分排序仅考虑具有显式调用参数的参数,因此会忽略某些参数(即函数参数包,具有默认参数的参数和省略号参数).[...] [ 示例:

    template<class T, class... U> void f(T, U ...);  // #1
    template<class T            > void f(T       );  // #2
    
    void h(int i) {
      f(&i); // error: ambiguous
      // [...]
    }
    
    Run Code Online (Sandbox Code Playgroud)

    但是,这不是最佳的.可变参数模板部分排序存在核心问题1395:

    CWG同意应该接受该示例,将此案例作为后期决胜局处理,更喜欢在参数包上省略参数.

    (问题1825提供了更精确的策略.)两个编译器都针对第一种情况实施此规则; 只有GCC会为第二个做(即可以考虑提前半步).