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

时间:2013-05-07 00:59:34

标签: c++ templates c++11 variadic-templates

鉴于此代码,模板参数推导如何决定最后一次函数调用?

#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"
}

(1)似乎很简单。它不能推断出返回类型,因此使用了void版本。

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

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

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


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

这是使用GCC 4.8.0编译的。为方便起见,这是test run上的Coliru

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

1 个答案:

答案 0 :(得分: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);
}

输出:

main.cpp: In function 'int main()':  
main.cpp:15:15: error: call of overloaded 'foo(int)' is ambiguous

Live example.