Minimal program:
#include <stdio.h>
#include <type_traits>
template<typename S, typename T>
int foo(typename T::type s) {
return 1;
}
template<typename S, typename T>
int foo(S s) {
return 2;
}
int main(int argc, char* argv[]) {
int x = 3;
printf("%d\n", foo<int, std::enable_if<true, int>>(x));
return 0;
}
output:
1
Why doesn't this give a compile error? When the template code is generated, wouldn't the functions int foo(typename T::type search)
and int foo(S& search)
have the same signature?
If you change the template function signatures a little bit, it still works (as I would expect given the example above):
template<typename S, typename T>
void foo(typename T::type s) {
printf("a\n");
}
template<typename S, typename T>
void foo(S s) {
printf("b\n");
}
Yet this doesn't and yet the only difference is that one has an int signature and the other is defined by the first template parameter.
template<typename S, typename T>
void foo(typename T::type s) {
printf("a\n");
}
template<typename S, typename T>
void foo(int s) {
printf("b\n");
}
Compiler error (Clang):
test.cpp:26:2: error: call to 'foo' is ambiguous
foo<std::enable_if<true, int>>(3);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:16:6: note: candidate function [with T = std::__1::enable_if<true, int>]
void foo(typename T::type s) {
^
test.cpp:21:6: note: candidate function [with T = std::__1::enable_if<true, int>]
void foo(int s) {
^
1 error generated.
I'm using code similar to this for a project I'm working on and I'm afraid that there's a subtly to the language that I'm not understanding that will cause some undefined behavior in certain cases. I should also mention that it does compile on both Clang and in VS11 so I don't think it's just a compiler bug.
Edit: Corrected second case (typo); added error message from Clang.
Edit #2: For those of you that asked what T::type means.
From http://en.cppreference.com/w/cpp/types/enable_if:
template< bool B, class T = void > struct enable_if;
If B is true, std::enable_if has a public member typedef type, equal to T; otherwise, there is no member typedef.
enable_if is a struct. Basically, if the expression evaluated in the first template parameter of enable_if is true (and in the case of my examples above, it is), then then there will be a public member type
that has the same type as the second template parameter.
In the case of enable_if<true, int>
, enable_if::type has a type of int.
You may overload a function template either by a non-template function or by another function template. The function call f(1, 2) could match the argument types of both the template function and the non-template function.
The name of the function templates are the same but called with different arguments is known as function template overloading. If the function template is with the ordinary template, the name of the function remains the same but the number of parameters differs.
Function overloading is used when multiple functions do similar operations; templates are used when multiple functions do identical operations. Templates provide an advantage when you want to perform the same action on types that can be different.
the advantage of templates in a situation where you want to do the same set of operations on many different data types, is that the compiler will handle for you at compile time any possible new type you may create in the future that uses the templated function.
If you call the name of an overloaded function template, the compiler will try to deduce its template arguments and check its explicitly declared template arguments. If successful, it will instantiate a function template specialization, then add this specialization to the set of candidate functions used in overload resolution.
A template is a tool that reduces the efforts in writing the same code as templates can be used at those places. A template function can be overloaded either by a non-template function or using an ordinary function template. Function Overloading: In function overloading, the function may have the same definition, but with different arguments.
The compiler proceeds with overload resolution, choosing the most appropriate function from the set of candidate functions. Non-template functions take precedence over template functions. The following example describes this:
In Function Overloading “Function” name should be the same and the arguments should be different. Function overloading can be considered as an example of polymorphism feature in C++.
The first function is considered to be more specialized than the first.
The function
int foo(typename T::type)
could match
template <typename S,typename T> int foo(S s)
by using T::type as the value for parameter S, but
int foo(S s)
will not match
template <typename S,typename T> int foo(typename T::type)
because T cannot be deduced.
The logic is layed out in the C++03 standard in section 14.5.5.2, and in the C++11 standard in section 14.5.6.2.
Here is the idea: To see if one function is more specialized than another, you invent values for each template parameter for the first function, and then see if the second function could match the resulting signature. You also invent values for the template parameters of the second function, and see if the first function will match the resulting signature. If the second function can match the first one, then the second function can't be more specialized than the first one. If, in addition to that, the first function can't match the second, then the first must be more specialized than the second. That is the case you have.
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