Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does template argument deduction work in this case?

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.

like image 478
chris Avatar asked May 07 '13 00:05

chris


People also ask

What is template argument deduction?

Template argument deduction is used when selecting user-defined conversion function template arguments. A is the type that is required as the result of the conversion. P is the return type of the conversion function template.

What is a template argument?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

Can there be more than one argument to template?

Can there be more than one argument to templates? Yes, like normal parameters, we can pass more than one data type as arguments to templates.

What is type deduction?

Type inference or deduction refers to the automatic detection of the data type of an expression in a programming language. It is a feature present in some strongly statically typed languages.


1 Answers

I believe Clang to be correct (still in 3.3 SVN), the call in (4) is ambiguous according to partial ordering rules - both function template are viable and neither is more specialized than the other.

Note that GCC can be coerced into giving the same output by transforming the variadic packs into single parameters:

#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);
}

Output:

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

Live example.

like image 141
Xeo Avatar answered Sep 28 '22 14:09

Xeo