16

Given this code, how does template argument deduction decide what to do for the last function call?

#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) seems pretty straightforward. It can't deduce a return type, so the void version is used.

(2) explicitly states some of the arguments' types. The first template argument matches the first argument, the second template argument matches the second argument, and the third template argument is deduced. If the int was used for the return type, the char wouldn't match the first argument.

(3) does the same thing as (2), but the first types do not match. Therefore, that must be deduced as the return type and the two arguments used to deduce the two Args arguments.

(4) seems ambiguous. It explicitly specifies a template argument, just like (2) and (3). The template argument matches the argument, just like (2). However, it does not use that as the first and deduce the other two, but rather use the explicit template argument as the return type and deduce all three Args arguments.


Why does (4) seem to half follow (2), but then use the other version? The best guess I have is that the single template parameter being filled in is a better match than just the parameter pack. Where does the standard define this behaviour?

This was compiled using GCC 4.8.0. For convenience, here's a test run on Coliru.

On Clang 3.1, however, (4) does not compile, due to ambiguity (see comments). This opens up the possibility that one of these two compilers has a bug. Making this possibility more probable is the fact that the Visual Studio 2012 November CTP compiler gives the same result as Clang, with (4) being ambiguous.

4

1 回答 1

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

活生生的例子。

于 2013-05-07T08:19:08.237 回答