在这种情况下,模板参数推导如何工作?

chr*_*ris 16 c++ templates variadic-templates c++11

鉴于此代码,模板参数推导如何决定如何为最后一个函数调用做什么?

#include <iostream>

template<typename Ret, typename... Args>
Ret foo(Args&&...) {
    std::cout << "not void\n";
    return {};
}

template<typename... Args>
void foo(Args&&...) {
    std::cout << "void\n";
}

int main() {
    foo(3, 'a', 5.4);            //(1): prints "void"
    foo<int, char>(3, 'a', 5.4); //(2): prints "void"
    foo<int>('a', 5.4);          //(3): prints "not void"
    foo<int>(3, 'a', 5.4);       //(4): prints "not void"
}
Run Code Online (Sandbox Code Playgroud)

(1)看起来很简单.它不能推断出返回类型,因此使用了void版本.

(2)明确陈述一些参数的类型.第一个模板参数匹配第一个参数,第二个模板参数匹配第二个参数,推导出第三个模板参数.如果int用于返回类型,则char与第一个参数不匹配.

(3)与(2)做同样的事情,但第一种类型不匹配.因此,必须将其推断为返回类型和用于推导两个Args参数的两个参数.

(4)似乎含糊不清.它显式指定了模板参数,就像(2)和(3)一样.模板参数与参数匹配,就像(2)一样.但是,它不使用它作为第一个并推导出其他两个,而是使用显式模板参数作为返回类型并推导出所有三个Args参数.


为什么(4)似乎有一半跟随(2),但是然后使用其他版本?我最好的猜测是填充的单个模板参数是一个比参数包更好的匹配.标准在哪里定义了这种行为?

这是使用GCC 4.8.0编译的.为方便起见,这是对Coliru的测试.

但是,在Clang 3.1中,(4)由于含糊不清(见注释)而无法编译.这开启了这两个编译器中的一个有错误的可能性.使这种可能性更可能的是,Visual Studio 2012 November CTP编译器提供与Clang相同的结果,其中(4)不明确.

Xeo*_*Xeo 4

我相信 Clang 是正确的(仍然在 3.3 SVN 中),根据部分排序规则,(4) 中的调用是不明确的 - 两个函数模板都是可行的,并且没有一个比另一个更专业。

请注意,可以通过将可变参数包转换为单个参数来强制 GCC 提供相同的输出:

#include <iostream>

template<typename Ret, typename T>
Ret foo(T&&) {
    std::cout << "not void\n";
    return {};
}

template<typename T>
void foo(T&&) {
    std::cout << "void\n";
}

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

输出:

main.cpp: In function 'int main()':  
main.cpp:15:15: error: call of overloaded 'foo(int)' is ambiguous
Run Code Online (Sandbox Code Playgroud)

活生生的例子。