Consider the following code:
#include <iostream>
#include <type_traits>
// Variadic version
template<class... Variadic>
void f(const Variadic&... variadic)
{
std::cout<<"variadic"<<std::endl;
}
// Single version
template<class Single, class = typename std::enable_if<std::is_fundamental<Single>::value>::type>
void f(const Single& single)
{
std::cout<<"single"<<std::endl;
}
// Main
int main()
{
f(); // variadic
f(42); // single : why?
f(std::string()); // variadic
f(42, 42); // variadic
return 0;
}
I do not understand why the line marked "single" compiles well (under g++ 4.6.3) and does not produce an overload resolution problem. Does the c++11 standard say that a template function with a fixed number of parameters is prefered over a variadic function that could have the same signature ?
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.
A variadic template is a class or function template that supports an arbitrary number of arguments. This mechanism is especially useful to C++ library developers: You can apply it to both class templates and function templates, and thereby provide a wide range of type-safe and non-trivial functionality and flexibility.
Args declares an "object parameter" (pass by value) and Args&& declares a reference parameter (pass by reference). Passing by reference allows one to avoid copying the argument when that is unnecessary.
Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.
It is really quite simple (two live examples, gcc and clang)
template<class...T> void foo(T&&...) {std::cout << "...T\n";}
template<class T> void foo(T&&) {std::cout << "T\n";}
int main() {
foo(3);
}
Overloads not taking ...
seem to be preferred when the choice is an explicit template parameter.
The class=std::enable_if_t
does not change this.
So both your functions f
are candidates, then the compiler prefers the one without variardics.
/8:
If
A
was transformed from a function parameter pack andP
is not a parameter pack, type deduction fails. Otherwise, using the resulting typesP
andA
, the deduction is then done as described in14.8.2.5
. IfP
is a function parameter pack, the typeA
of each remaining parameter type of the argument template is compared with the type P of the declarator-id of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template. [ Example:
template<class... Args> void f(Args... args); // #1
template<class T1, class... Args> void f(T1 a1, Args... args); // #2
template<class T1, class T2> void f(T1 a1, T2 a2); // #3
f(); // calls #1
f(1, 2, 3); // calls #2
f(1, 2); // calls #3; non-variadic template #3 is more
// specialized than the variadic templates #1 and #
In particular, the f(1,2)
example.
All the enable_if_t
clause does is remove the one-argument version from consideration when you pass a std::string
as T
.
Due to the use of the second, enable_if, template parameter in the 'single' version, the compiler considers that version to be a more-specialized template for use with the types for which it is enabled.
It is considered more specialized because there are types where the variadic template can be instantiated, but the 'single' can't.
The general rule is that a more specialized template trumps a less specialized template in overload resolution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With