To study the Overloading of Function Templates, I have wrote two functions:
template <typename T>
void pe16_61_compare(const T&, const T&) {
cout <<"template pe16_61_compare(T, T) called" << endl;
}
// plain functions to handle C-style character strings
void pe16_61_compare(const char*, const char*) {
cout <<"normal func pe16_61_compare(...) called" << endl;;
}
and then I have defined some variables and called the function: pe16_61_compare
const char const_arr1[] = "world", const_arr2[] = "hi";
char ch_arr1[] = "world";
// first call
pe16_61_compare(ch_arr1, const_arr1);
// second call
pe16_61_compare(const_arr1, const_arr2);
the output result is:
template pe16_61_compare(T, T) called
normal func pe16_61_compare(...) called
What confuse me is that the first call invoke the template function. For me, for the first call, both two pe16_61_compare function are viable functions and the have the same rank of conversion (non-const to const and array to pointer), and it is said in this case, the template function should be removed from the set of viable functions.
Could any one tell me why?
Thank you for considering my question!
For the first call, T = char[6] is a better match than the conversion to char const *, so the template wins.
For the second call, no single template parameter can work for arrays, so the non-template overload is the only matching one.
If you say,
pe16_61_compare(static_cast<char const *>(const_arr_1),
static_cast<char const *>(const_arr_2));
then both the template instance and the ordinary function are viable and have the same signature, but as a tie breaker the non-template function wins (i.e. this isn't ambiguous).
Related
I have this example from C++ primer 5th ed:
template <typename T>
int compare(T const& x, T const& y) // in the book const T&
{
cout << "primary template\n"; // I've added print statements
if(std::less<T>()(x, y))
return -1;
if(std::less<T>()(y, x))
return 1;
return 0;
}
template <unsigned N, unsigned M>
int compare(char const(&rArr1)[N], char const(&rArr2)[M])
{
cout << "overload for arrays\n";
return strcmp(rArr1, rArr2);
}
template <>
int compare(char const* const &p1, char const* const &p2)
{
cout << "specilization for char* const\n";
return strcmp(p1, p2);
}
int main()
{
std::cout << compare("Hi", "Mom") << '\n';
}
"Whether we define a particular function as a specialization or as an independent, non-template function can impact function matching. For example, we have defined two versions of our compare function template, one that takes references to array parameters and the other that takes const T&. The fact that we also have a specialization for character pointers has no impact on function matching. When we call compare on a string literal:
compare("hi", "mom");
both function templates are viable and provide an equally good (i.e., exact) match to the call. However, the version with character array parameters is more specialized (§ 16.3, p. 695) and is chosen for this call."
I think the it is incorrect: "compare("hi", "mom"); both templates are viable" because as I see the first version that takes T const&, T const& is not viable because it is instantiated as:
compare(char const(&rArra1)[3], char const(&rArr2)[4]);
In which case sizes are different and as we know the size of an array is a part of its type thus here the two literal strings have two different types thus the Template Argument Deduction will fail thus this instantiation is rejected. it is as if we wrote: compare(5, 0.); // int, double so the compiler cannot deduce a unique type as it is required here.
To confirm what I am saying if I comment out the version taking array types then the code won't compile for that call with two literal strings with different sizes.
What confuses me too is that if I comment out the arrays-type version the code doesn't compile even if I have the third version (taking pointer to character strings)! Is this because of the fact that a Specialization doesn't affect function matching? and what was rejected by a primary template is rejected by all of its specializations?
Please clarify. Thank you!
To prevent any confusion, I very much understand the difference between arrays and pointers, the concept of decay-to-pointer, and the concept of passing an array by reference in C++, etc.
My question here is specifically about the rules used by the compiler to select a function from a set of function overload candidates, when one overload takes an array reference, and the other overload takes a pointer.
For example, suppose we have:
template <class T, std::size_t N>
void foo(const T (&arr)[N])
{
std::cout << "Array-reference overload!" << std::endl;
}
template <class T>
void foo(const T* ptr)
{
std::cout << "Pointer overload!" << std::endl;
}
If we attempt to invoke function template foo() as follows:
const char arr[2] = "A";
foo(arr);
... then my expectation would be that the first overload, the one that takes an array reference, would be selected by the compiler.
However, using GCC 4.9.2, if I compile the above code, I get an error:
test.cpp:28:9: error: call of overloaded ‘foo(const char [2])’ is ambiguous
It's unclear to me why both overloads are considered equally good candidates by the compiler here, since the first overload matches the type exactly, whereas the second overload requires an extra decay-to-pointer step.
Now, I am able to get the above overload working by explicitly using type_traits as follows:
template <class T, std::size_t N>
void foo(const T (&arr)[N])
{
std::cout << "Array-reference overload!" << std::endl;
}
template <class T>
void foo(T ptr, typename std::enable_if<std::is_pointer<T>::value>::type* = 0)
{
std::cout << "Pointer overload!" << std::endl;
}
In this case, the program compiles and the overload that takes an array reference is selected. However, I don't understand why this solution should be necessary. I'd like to understand why the compiler considers a function that requires decay-to-pointer an equally likely overload candidate as the array reference, when the argument passed is very much an array.
the first overload matches the type exactly, whereas the second overload requires an extra decay-to-pointer step.
Because when checking the ranking of implicit conversion sequences in overload resolution, the array-to-pointer conversion is considered as an exact match, thus the 2nd overload has the same rank with the 1st one.
From the standard, $16.3.3.1.1 Standard conversion sequences [over.ics.scs] Table 13 — Conversions
Conversion Category Rank Subclause
No conversions required Identity Exact Match
... ...
Array-to-pointer conversion Lvalue Transformation Exact Match [conv.array]
... ...
It's worth noting that the rank of "No conversions required" (i.e. the case for the 1st overload) is "Exact Match" too.
I have a very strange problem. To keep things simple, lets say I want to have a function which takes 2 functions with the same declaration as arguments
template<typename Func>
void foo(Func a, Func b)
{
std::cout << "good";
}
To try things out I took putchar from cstdio, and created an identical function to match the putchar.
int myPutcharFunc(int)
{
return 0;
}
int main()
{
auto myPutcharLambda = [](int) -> int
{
return 0;
};
foo(putchar, myPutcharFunc); // okay
foo(putchar, myPutcharLambda); //deduced conflicting types for parameter 'Func' ('int (__attribute__((__cdecl__)) *)(int)' and 'main()::<lambda(int)>')
}
Now, the lambda does not want to compile (the key is I want to use lambda capture).
So lets add template specialization, because the programmer is wiser than the machine, right? :)
template<typename Func>
void foo(Func a, Func b)
{
std::cout << "good";
}
template<>
void foo(int(*)(int), int(*)(int))
{
std::cout << "good";
}
No luck, the same error - why?
But for some reason, when I comment out the template specialization:
//template<>
void foo(int(*)(int), int(*)(int))
{
std::cout << "good";
}
The code compiles. I obviously do not want to overload foo for EVERY set of function's arguments - thats what templates are for. Every step was tested both with msvc++ and g++. What am I doing wrong?
Two possibilities.
1: Just put + in front of the lambda:
foo(putchar, +myPutcharLambda);
That works because unary + expects an integer-like value, such as a pointer. Therefore, the lambda converts to a function pointer.
Ultimately a (non-capturing) lambda doesn't have the same type as a function pointer, even though it's willing to convert to a function pointer.
How is a compiler supposed to know which conversions are allowed to make two objects of the same type?
2: There is another option, making use of the fact that the ?: is willing to do some conversions, converting one type to another in some circumstances.
template<typename Func1, typename Func2>
void foo2(Func1 a, Func2 b)
{
using common_type = decltype(true?a:b); // or 'false', it doesn't matter
foo<common_type>(a,b);
}
Every lambda is a different type, so you'll need to have two different template parameters to get them
template<typename FuncA, typename FuncB>
void foo(FuncA a, FuncB b)
Types don't decay when deducing template types (SEE COMMENT FOR CORRECTION). So a lambda remains a lambda and doesn't decay to a function pointer. Same reason a string literal is deduced as a char[N] instead of a const char *.
With your second example using specialization, it doesn't want to use your specialization, since the lambda is not a function pointer. You can cast the Lambda to a function pointer and make it work: https://godbolt.org/g/ISgPci The trick you can do here is say +my_lambda because + is defined for pointers so it will force the non-capturing lambda to become a function pointer.
A lambda has its own type which can decay to a function pointer but not in the case of a template function match, it will for the real function as you found because of the implicit conversion.
In the case of matching to a template you need to disambiguate and explicitly instantiate foo with the type you want or convert the lambda to a function pointer.
foo<decltype(putchar)>(putchar, myPutcharLambda);
or
foo(putchar, +myPutcharLambda);
I'm playing around with variadic templates, and wrote this, based on this answer :
template <size_t... I>
void print(seq<I...>)
{
decltype(std::cout) * dummy[sizeof...(I)] = { &(std::cout << I << ' ')... };
}
Because std::cout::operator<< has a return type, it can be stored, so there's no need for the ( ,0) comma trick.
Now, to shut up the "unused variable 'dummy'" warning, and to print a newline, I tried the following statements, but they didn't do what I wanted:
dummy[0]->operator <<('\n'); // prints 10
(apparently called operator<<(int) instead of operator<<(char)
dummy[0]->operator <<("\n"); // prints a pointer
(apparently called operator<<(const void*) instead of operator<<(const char*)
In the end, I had to write
*dummy[0] << '\n'; // prints a newline as desired
My question is, why did the "wrong" overloads get chosen?
The "wrong" overloads are chosen because only some overloads are members of std::ostream class. The overloads for char and const char* are not members of std::ostream, but free functions, so, in
*dummy[0] << '\n';
argument-dependent lookup will find operator<<(std::ostream&, char), but in
dummy[0]->operator <<('\n');
only member functions will be considered, resulting in std::ostream::operator<<(int) being called.
Can i actually use a function overloading like this:
#include <iostream>
void foo(...)
{
std::cout << "::foo(...) \n";
}
void foo(int)
{
std::cout << "::foo(int) \n";
}
int main()
{
foo(0);
foo('A');
foo("str");
foo(0, 1);
}
What standard says about it? And in what kind of situations i'll get ::foo(...)?
void foo(int)
will accept one argument of type int.
void foo(...)
accepts any number of arguments, of any type. It will be selected when the call doesn't have a single int argument. Not very useful, in general.
Also note that it is undefined behavior to pass objects of class type to ....
In N3337 I can see:-
13.3.2 Viable functions
A candidate function having fewer than m parameters is viable only if
it has an ellipsis in its parameter list (8.3.5). For the purposes of
overload resolution, any argument for which there is no corresponding
parameter is considered to “match the ellipsis” (13.3.3.1.3) .
When you declare a function in the following way :
void foo (...)
this mean foo accepts any number of arguments.
So this function will be called when this is the must suitable one.
In your case whenever you won't write :
foo(//Some single int).
In your specific main, this will happen :
foo(0) //Calls foo(int).
foo('A) //Calls foo (int). as you can convert a char to an int.
foo("str") //Calls foo(...) . as you can not convert a string to an int.
foo(1,2) //Calls foo(...) . as this is the only possible function
cause the second foo function only takes one int.
void foo(...) will take variable arguments. And it will be called when there the no or type of argument does not match the provided argument list of other function with the same function name.
foo(0); //This will call void foo(int) function
foo('A'); //This will call void foo(int) function
foo("str"); //This will call void foo(...) function
foo(0, 1); //This will call void foo(...) function
NOTE:
Although the ellipsis works fine with function overloading, it is not highly recommend pursuing variadic functions. At least not until you have significantly more experience in C++ to understand the pitfalls. I would suggest its use only with the try catch block where there are situations when the error cannot be predicted.