I am trying to understand the overload resolution rules in the following case:
template<typename T>
void f(const T& x) {
std::cout << __PRETTY_FUNCTION__ << std::endl; //-
}
template<typename T>
void f(T& x) { // <> Überladung Variante 2
std::cout << __PRETTY_FUNCTION__ << std::endl; //-
}
int main()
{
int e1 = 0;
f(e1);
const int e2 = 0;
f(e2);
}
The output is:
void f(T &) [T = int]
void f(const T &) [T = int]
As I understand in the first call to f(e1) leads to the viable functions
void f(const int&)
void f(int&)
from which the first one is chosen because the const-qualification hasn't to be removed.
The second call to f(e2) leads to the type deductions / viable functions
void f(const int&); // T -> int from first template overload
void f(const int&); // T -> const int from second overload
and the output shows that the first overload is choosen.
But why?
When performing type deduction with references, the const-ness (more specifically CV-ness) is not removed. So in your case the compiler has 2 overloads to choose from:
void f(const T &)
void f(T &)
The compiler then performs "pattern matching" when choosing the overload for your const int e2 = 0; argument. The first const overload is a better match (more specialized), as the second one would require deducing T as const int, which adds something (i.e. const-ness).
The rules for template type deductions are not super straightforward, so if you want to learn all nitty-gritty details about templates, I highly recommend the book
C++ Templates: The Complete Guide by David Vandevoorde and Nicolai M. Josuttis.
It's pre C++11, but nevertheless it tells you everything you can think of.
PS: you must make a differentiation between instantiation and template type deduction. The type deduction happens first, then an instantiation follows. So in your case you don't have 2 ambiguous instantiations as you may have thought initially.
Related
I have common code – Dijkstra's algorithm – I use in different contexts, so I decided to use tag dispatch.
The common code is in the following function (you can see End get dispatched depending on the Tag template parameter):
template <typename Tag, typename ... Args>
void Dijkstra(blahblah, Args&&... arg) {
...
if (End(Tag(), cost, n_id, distances, time_limit, args ...)) {
break;
}
For most of the contexts I define a default no-op as follows:
template<typename ... Args>
bool inline End(Args&& ...) {
return false;
}
For one context I define the function with the following signature:
bool inline End(OneContextTag, Duration d, NodeId n_id, Distances distances, Du time_limit, blahblah) {
Everything worked as expected, till I found I forgot & in the signature after Distances – I was copying Distances, a large unordered_map, every time.
However, after I changed it to const Distances& to avoid expensive copying, the less specialized noop version got called. I have no idea why. And how to fix it.
(I swear the change is only in adding a single character &. Or const&)
(The signature is otherwise correct, if I comment out the generic noop version, it just uses the OneContextTag version.)
(The code is more complex, but I hope it can be figured out from this.)
So what you're asking about is basically why the following program prints Special foo but Generic bar:
struct A {};
template<class ... Args>
void foo(Args&&...)
{
std::cout << "Generic foo\n";
}
void foo(A)
{
std::cout << "Special foo\n";
}
template<class ... Args>
void bar(Args&&...)
{
std::cout << "Generic bar\n";
}
void bar(A const&)
{
std::cout << "Special bar\n";
}
int main()
{
A a;
foo(a);
bar(a);
}
Let's look at what happens for overload resolution:
1. Candidate functions are selected.
C++11/[over.match.funcs]/7 In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way.
Candidates for call to foo(a):
template<> void foo<A&>(A&); // reference collapsing
void foo(A);
Candidates for call to bar(a):
template<> void bar<A&>(A&);
void bar(A const&);
2. Select of best viable function:
In the first place, an overload is better if (at least) one of the parameters has a better conversion sequence (and no other has a worse conversion sequence).
C++11/[over.ics.rank]/3 Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [ ... ] S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.
This results in the preference of the template candidate for bar since the conversion required to call void bar(A const&) requires binding an lvalue to an more cv-qualified const lvalue reference.
Therefore, you see the generic version called when using Distances const&.
C++11/[over.best.ics]/6 When the parameter type is not a reference [ ... ]
When the parameter has a class type and the argument expression has the same type, the implicit conversion sequence is an identity conversion.
This makes the conversion sequence for the parameter a when passed to void foo(A) an identity conversion (which is also the case for the template function).
If neither of the overloads has a better conversion sequence, then the non-template version wins over the template.
C++11/[over.match.best]/1 [ ... ] Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if [ ... ] F1 is a non-template function and F2 is a function template specialization.
This is the case for foo and makes your code behave as you intended when you use Distances distances.
I do not have an answer to why overload resolution works the way it does here atm. But I have a potential solution for you, which is also (IMO) more robust:
Change the default End to accept a UseDefaultEnd tag as the first parameter. For each context which should use the default End, subclass its tag from UseDefaultEnd:
#include <iostream>
struct UseDefaultEnd {};
/* Comment first parameter; then you get the same behavior as
you're currently trying to solve. */
template<typename ... Args>
bool inline End(UseDefaultEnd, Args&& ...) {
// bool inline End(Args&& ...) {
return false;
}
struct OneTag {};
struct OtherTag : public UseDefaultEnd {};
bool inline End(OneTag, int const & i) {
return true;
}
template<typename Tag>
void Caller() {
int i = 42;
if (End(Tag(), i)) {
std::cout << "Used specific version of End" << std::endl;
}
}
int main() {
Caller<OtherTag>();
std::cout << "---" << std::endl;
Caller<OneTag>();
}
Suppose I have two overloads of a function
template <typename T>
void f(const T&) {
cout << "f(T&)" << endl;
}
template <typename T>
void f(const T*) {
cout << "f(T*)" << endl;
}
Why does f(new int) resolves to the f(const T&) instead of f(const T*)? Anywhere in the standard talks about this counter-intuitive behavior?
http://ideone.com/kl8NxL
For overload resolution with template deduction, the first step is to resolve the templates. Then non-template ordering is applied to the results. In your code the template resolutions are:
void f(int * const &) // 1
void f(int const *) // 2
According to C++14 [over.ics.ref], a reference binding directly to an argument as in (1) is an identity conversion (even if there are added cv-qualifiers). The binding of T to T const & is a direct binding, i.e. no temporaries are created and bound.
However, (2) involves a qualification conversion. The argument type int * must be converted to const int * before it matches the function parameter.
The identity conversion is considered a sub-sequence of any non-identity conversion sequence, so (1) wins according to the sub-sequence rule [over.ics.rank]/3.1.1
Preamble
Overload resolution in C++ can be an overly complex process. It takes quite a lot of mental effort to understand all of the C++ rules that govern overload resolution. Recently it occurred to me that the presence of the name of an overloaded function in the argument list can add to the complexity of overload resolution. Since it happened to be a widely used case, I posted a question and received an answer that allowed me to better understand the mechanics of that process. However, the formulation of that question in the context of iostreams seems to have somewhat distracted the focus of the answers from the very essence of the problem being addressed. So I started delving deeper and came up with other examples that ask for more elaborate analysis of the issue. This question is an introductory one and is followed by a more sophisticated one.
Question
Assume that one fully understands how overload resolution works in the absence of arguments that are themselves names of overloaded functions. What amendments must be made to their understanding of overload resolution, so that it also covers cases where overloaded functions are used as arguments?
Examples
Given these declarations:
void foo(int) {}
void foo(double) {}
void foo(std::string) {}
template<class T> void foo(T* ) {}
struct A {
A(void (*)(int)) {}
};
void bar(int x, void (*f)(int)) {}
void bar(double x, void (*f)(double)) {}
void bar(std::string x, void (*f)(std::string)) {}
template<class T> void bar(T* x, void (*f)(T*)) {}
void bar(A x, void (*f2)(double)) {}
Below expressions result in the following resolution of the name foo (at least with gcc 5.4):
bar(1, foo); // foo(int)
// but if foo(int) is removed, foo(double) takes over
bar(1.0, foo); // foo(double)
// but if foo(double) is removed, foo(int) takes over
int i;
bar(&i, foo); // foo<int>(int*)
bar("abc", foo); // foo<const char>(const char*)
// but if foo<T>(T*) is removed, foo(std::string) takes over
bar(std::string("abc"), foo); // foo(std::string)
bar(foo, foo); // 1st argument is foo(int), 2nd one - foo(double)
Code to play with:
#include <iostream>
#include <string>
#define PRINT_FUNC std::cout << "\t" << __PRETTY_FUNCTION__ << "\n";
void foo(int) { PRINT_FUNC; }
void foo(double) { PRINT_FUNC; }
void foo(std::string) { PRINT_FUNC; }
template<class T> void foo(T* ) { PRINT_FUNC; }
struct A { A(void (*f)(int)){ f(0); } };
void bar(int x, void (*f)(int) ) { f(x); }
void bar(double x, void (*f)(double) ) { f(x); }
void bar(std::string x, void (*f)(std::string)) { f(x); }
template<class T> void bar(T* x, void (*f)(T*)) { f(x); }
void bar(A, void (*f)(double)) { f(0); }
#define CHECK(X) std::cout << #X ":\n"; X; std::cout << "\n";
int main()
{
int i = 0;
CHECK( bar(i, foo) );
CHECK( bar(1.0, foo) );
CHECK( bar(1.0f, foo) );
CHECK( bar(&i, foo) );
CHECK( bar("abc", foo) );
CHECK( bar(std::string("abc"), foo) );
CHECK( bar(foo, foo) );
}
Let's take the most interesting case,
bar("abc", foo);
The primary question to figure out is, which overload of bar to use. As always, we first get a set of overloads by name lookup, then do template type deduction for each function template in the overload set, then do overload resolution.
The really interesting part here is the template type deduction for the declaration
template<class T> void bar(T* x, void (*f)(T*)) {}
The Standard has this to say in 14.8.2.1/6:
When P is a function type, pointer to function type, or pointer to member function type:
If the argument is an overload set containing one or more function templates, the parameter is treated as a non-deduced context.
If the argument is an overload set (not containing function templates), trial argument deduction is attempted using each of the members of the set. If deduction succeeds for only one of the overload set members, that member is used as the argument value for the deduction. If deduction succeeds for more than one member of the overload set the parameter is treated as a non-deduced context.
(P has already been defined as the function template's function parameter type including template parameters, so here P is void (*)(T*).)
So since foo is an overload set containing a function template, foo and void (*f)(T*) don't play a role in template type deduction. That leaves parameter T* x and argument "abc" with type const char[4]. T* not being a reference, the array type decays to a pointer type const char* and we find that T is const char.
Now we have overload resolution with these candidates:
void bar(int x, void (*f)(int)) {} // (1)
void bar(double x, void (*f)(double)) {} // (2)
void bar(std::string x, void (*f)(std::string)) {} // (3)
void bar<const char>(const char* x, void (*f)(const char*)) {} // (4)
void bar(A x, void (*f2)(double)) {} // (5)
Time to find out which of these are viable functions. (1), (2), and (5) are not viable because there is no conversion from const char[4] to int, double, or A. For (3) and (4) we need to figure out if foo is a valid second argument. In Standard section 13.4/1-6:
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type is identical to the function type of the target type required in the context. The target can be
...
a parameter of a function (5.2.2),
...
... If the name is a function template, template argument deduction is done (14.8.2.2), and if the argument deduction succeeds, the resulting template argument list is used to generate a single function template specialization, which is added to the set of overloaded functions considered. ...
[Note: If f() and g() are both overloaded functions, the cross product of possibilities must be considered to resolve f(&g), or the equivalent expression f(g). - end note]
For overload (3) of bar, we first attempt type deduction for
template<class T> void foo(T* ) {}
with target type void (*)(std::string). This fails since std::string cannot match T*. But we find one overload of foo which has the exact type void (std::string), so it wins for the overload (3) case, and overload (3) is viable.
For overload (4) of bar, we first attempt type deduction for the same function template foo, this time with target type void (*)(const char*) This time type deduction succeeds, with T = const char. None of the other overloads of foo have the exact type void (const char*), so the function template specialization is used, and overload (4) is viable.
Finally, we compare overloads (3) and (4) by ordinary overload resolution. In both cases, the conversion of argument foo to a pointer to function is an Exact Match, so neither implicit conversion sequence is better than the other. But the standard conversion from const char[4] to const char* is better than the user-defined conversion sequence from const char[4] to std::string. So overload (4) of bar is the best viable function (and it uses void foo<const char>(const char*) as its argument).
I have the following code:
#include <iostream>
template <typename T>
void f(T& x)
{
std::cout << "f(T& )" << std::endl;
}
template <typename T>
void f(const T& x)
{
std::cout << "f(const T& )" << std::endl;
}
int main()
{
int a = 0;
const float b = 1.1;
f(a); // call f(T&)
f(b); // call f(const T&)
}
The output is:
f(T& )
f(const T& )
My question is: how does the compiler know which function to call? If I remove the references from the function definitions then I get an "ambiguous call" type of error, i.e. error: redefinition of 'f'. For me it looks like f(T&) can be equally well used for both calls, why is the const version unambiguously called for f(b)?
Given two competing overloads, the standard requires the compiler to select the overload that has the "best fit". (If there's no unique best overload, or if the unique best overload is inaccessible, the program is ill-formed.)
In this case, the rules are provided by §13.3.3.2 [over.ics.rank]/p3:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if:
[...]
S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.
This is the example given in the standard:
int f(const int &);
int f(int &);
int g(const int &);
int g(int);
int i;
int j = f(i); // calls f(int &)
int k = g(i); // ambiguous
In your case, const T& is more cv-qualified than T&, so by the standard, f(T&) is a better fit than f(const T&) and is selected by overload resolution.
f(T&) vs. f(T const&)
The two functions are different, in that the first signature states that any variable passed by reference may be modified by the function. So the const float cannot be passed to the first function, and the second is the only viable choice for the compiler. A nonconst variable could be passed to both, so the compiler has to chose the better fit, if there is one. The standard says, that in order to call the second function, the compiler would have to add a const to any nonconst variable, while for the first function this is not necessary. Adding const is an implicit conversion, and it is a "worse" converison (read that as more conversion steps) than adding nothing. Therefore the standard demands that the compiler picks the first function when passing nonconst variables.
In case you wonder: literals and temporaries can not be bound to nonconst references, so f(4), f("meow") and f(someFunc()) will all call the second function.
f(T) vs. f(const T)
They look different, but aren't in terms of overload resolution or function signature. Both of them are call by value, or for the compiler: pass a copy of the argument into the function. The only difference is in a function definition, that you require the variable to be constant in the function body. Any function declaration does not affect the variable definition in the function definition's signature:
void f(int); //a declaration
void f(int i); //redeclaration of the same function
void f(int const); //still the same function redeclared
void f(int const i2); //yes... a redeclaration
void f(int const i) { //at last a function definition and the copy of the argument used in the function body is required to be const
//...
}
void f(int i) { //there is only one f, so this is a redefinition!
//...
}
This is not an "ambuguos call type error", because for the compiler there is only one function and no ambiguity. The error is simply that you did defin the same funciton twice. For that reason, it is preferred in many style guides that function declarations have no top-level const, and compilers will often ignore them and not mention them in error or warning messages.
If you have this function
template<typename T> f(T&);
And then try to call it with, let's say an rvalue like
f(1);
Why isn't T just be deduced to be const int, making the argument a const int& and thus bindable to an rvalue?
This is mentioned as a potential solution in the document I linked in the recent C++0x forwarding question.
It would work fairly well, but it breaks existing code. Consider (straight from the document):
template<class A1> void f(A1 & a1)
{
std::cout << 1 << std::endl;
}
void f(long const &)
{
std::cout << 2 << std::endl;
}
int main()
{
f(5); // prints 2 under the current rules, 1 after the change
int const n(5);
f(n); // 1 in both cases
}
Or
// helper function in a header
template<class T> void something(T & t) // #1
{
t.something();
}
// source
#include <vector>
void something(bool) // #2
{
}
int main()
{
std::vector<bool> v(5);
// resolves to #2 under the current rules, #1 after the change
something(v[0]);
}
This also fails to forward the value category (lvalue or rvalue), which isn't much of a problem in C++03. But since this fix could only be done during C++0x, we'd effectively shutting ourselves out from rvalue references when forwarding (a bad thing). We should strive for a better solution.
It is, but only if you declare f to take T const &.
template <typename T> void f(T &);
template <typename T> void g(T const &);
void x() { f(1); } // error: invalid initialization of non-const reference
void y() { g(1); } // no error
And if you declare both f(T &) and f(T const &), it'll choose the const-qualified one:
template <typename T> void f(T &);
template <typename T> void f(T const &);
void x() { f(1); } // no error, calls f(T const &)
Now maybe you're saying “in the first example, why does it generate a temporary of type int for the call to f when it could have generated a temporary of type const int and made the code compile?” The best answer I have for you is that that would be inconsistent with the overload resolution behavior when the argument isn't an integer constant.