Overload resolution, templates and inheritance - c++

#include <iostream>
struct A {};
struct B : public A {};
template<typename T>
void foo(const T &x) { std::cout << "Called template" << std::endl; }
void foo(const A &a) { std::cout << "Called A" << std::endl; }
int main()
{
foo(A());
foo(B());
return 0;
}
This prints:
Called A
Called template
I was under the impression that a suitable non-template function would always be chosen over a template function. Can someone explain to me the resolution steps that lead to this somewhat surprising result?

I was under the impression that a suitable non-template function would always be chosen over a template function.
This only holds if the template and the non-template are equally good candidates. That's why the non-template is chosen for foo(A()).
However, in the case of foo(B()), using the non-template requires a derived-to-base conversion. So the function template is strictly better, and hence it's chosen.
The foo template instantiates into void foo(const B&). Consider what it would look like without templates:
void foo(const B &x) { std::cout << "Called template" << std::endl; }
void foo(const A &a) { std::cout << "Called A" << std::endl; }
I believe you'll agree calling foo(B()) should unambiguously pick the first one. That's exactly why the template is chosen.

n3376 13.3.3.1/6
When the parameter has a class type and the argument expression has a
derived class type, the implicit conversion sequence is a
derived-to-base Conversion from the derived class to the base class.
n3376 13.3.3.1/8
If no conversions are required to match an argument to a parameter
type, the implicit conversion sequence is the standard conversion
sequence consisting of the identity conversion (13.3.3.1.1).
Identity conversion has exact match rank due table in 13.3.3.1.1/table 12, but derived-to-base is worse, than identity.
So, compiler just have candidates in first case
// template after resolving
void foo(const A&)
// non-template
void foo(const A&)
Both has identity rank, but since first is function-template, second will be chosen.
And in second case
// template after resolving
void foo(const B&)
// non-template
void foo(const A&)
Only first has identity rank and will be chosen.

Can someone explain to me the resolution steps that lead to this somewhat surprising result?
you may look at Overload Resolution at cppreference.com:
http://en.cppreference.com/w/cpp/language/overload_resolution
in particular see the section Ranking of implicit conversion sequences
Extension of the Answer:
I tried to provide more clarification with an excerpt of the information from the aforementioned link:
A function template by itself is not a type, or a function, or any other entity. No code is generated from a source file that contains only template definitions. In order for any code to appear, a template must be instantiated: the template arguments must be determined so that the compiler can generate an actual function (or class, from a class template).
For that, the compiler goes through:
function template name lookup
template argument deduction
Down to here, the compiler has a couple of candidate function definitions which can handle the specific function call. These candidates are instannces of the template function as well as relevant non-template function definitions in the program.
But the answer to your question lies in fact here:
Template argument deduction takes place after the function template name lookup (which may involve argument-dependent lookup) and before overload resolution.
The fact that function overload resolution is performed after template function instantiation is the reason for the ouput of your code.
Now your specific case goes through overload resolution as the following:
Overload Resolution:
If the [previous] steps produce more than one candidate function, then overload resolution is performed to select the function that will actually be called. In general, the candidate function whose parameters match the arguments most closely is the one that is called.
.
.
.
...
F1 is determined to be a better function than F2 if implicit conversions for all arguments of F1 are not worse than the implicit conversions for all arguments of F2, and
1) there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2
...
.
.
.
Ranking of implicit conversion sequences:
Each type of standard conversion sequence is assigned one of three ranks:
1) Exact match: no conversion required, lvalue-to-rvalue conversion, qualification conversion, user-defined conversion of class type to the same class
2) Promotion: integral promotion, floating-point promotion
3) Conversion: integral conversion, floating-point conversion, floating-integral conversion, pointer conversion, pointer-to-member conversion, boolean conversion, user-defined conversion of a derived class to its base
The rank of the standard conversion sequence is the worst of the ranks of the standard conversions it holds (there may be up to three conversions)
Binding of a reference parameter directly to the argument expression is either Identity or a derived-to-base Conversion:
struct Base {};
struct Derived : Base {} d;
int f(Base&); // overload #1
int f(Derived&); // overload #2
int i = f(d); // d -> Derived& has rank Exact Match
// d -> Base& has rank Conversion
// calls f(Derived&)

Related

Overload resolution and user-defined conversion

Consider this example:
struct Foo
{
Foo(int){cout << "Foo(int)\n";}
Foo(double){cout << "Foo(double)\n";}
operator int()const{cout << "operator int()\n"; return 0;}
operator double()const{cout << "operator double()\n"; return 0.;}
};
void bar(Foo){cout << "bar(Foo)\n";}
void bar(float){cout << "bar(float)\n";}
int main()
{
int i = 5;
bar(i); // whey bar(float) and not bar(Foo)?
}
I know I shouldn't overload the "converting-ctor" to take relate types (here arithmetic types) but just for understanding better function matching and user-defined-conversion.
Why the call to bar is resolved to bar(float) and not bar(Foo) as long as Foo has an exact match for this argument (int)?
Does it mean that standard conversion is preferred over user-defined conversion?
Does it mean that standard conversion is preferred over user-defined conversion?
Yes. Standard conversions are always preferred over user-defined ones. See this
In deciding on the best match, the compiler works on a rating system for the way the types passed in the call and the competing parameter lists match up. In decreasing order of goodness of match:
An exact match, e.g. argument is a double and parameter is a double
A promotion
A standard type conversion
A constructor or user-defined type conversion

std::variant converting constructor doesn't handle const volatile qualifiers

The code below:
int i = 1;
const int i_c = 2;
volatile int i_v = 3;
const volatile int i_cv = 4;
typedef std::variant<int, const int, volatile int, const volatile int> TVariant;
TVariant var (i );
TVariant var_c (i_c );
TVariant var_v (i_v );
TVariant var_cv(i_cv);
std::cerr << std::boolalpha;
std::cerr << std::holds_alternative< int>(var ) << std::endl;
std::cerr << std::holds_alternative<const int>(var_c ) << std::endl;
std::cerr << std::holds_alternative< volatile int>(var_v ) << std::endl;
std::cerr << std::holds_alternative<const volatile int>(var_cv) << std::endl;
std::cerr << var .index() << std::endl;
std::cerr << var_c .index() << std::endl;
std::cerr << var_v .index() << std::endl;
std::cerr << var_cv.index() << std::endl;
outputs:
true
false
false
false
0
0
0
0
coliru
And so std::variant converting constructor doesn't take into account const volatile qualifier of the converting-from type. Is it expected behavior?
Information about converting constructor from cppreference.com
Constructs a variant holding the alternative type T_j that would be selected by overload resolution for the expression F(std::forward<T>(t)) if there was an overload of imaginary function F(T_i) for every T_i from Types...
The problem is that in the case above the overload set of such imaginary function is ambiguous:
void F( int) {}
void F(const int) {}
void F( volatile int) {}
void F(const volatile int) {}
coliru
cppreference.com says nothing about this case. Does the standard specify this?
I'm making my own implementation of std::variant class. My implementation of converting constructor is based on this idea. And the result is the same as shown above (the first suitable alternative is selected, even though there are others). libstdc++ probably implements it in the same way, because it also selects the first suitable alternative. But I'm still wondering if this is correct behavior.
Yeah, this is just how functions work when you pass by value.
The function void foo(int) and the function void foo(const int) and the function void foo(volatile int) and the function void foo(const volatile int) are all the same function.
By extension, there is no distinction for your variant's converting constructor to make, and no meaningful way to use a variant whose alternatives differ only in their top-level cv-qualifier.
(Well, okay, you can emplace with an explicit template argument, as Marek shows, but why? To what end?)
[dcl.fct/5] [..] After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. [..]
Note that you are creating copy of value. This means that const and volatile modifiers can be safely discarded. That is why template always deduces int.
You can force specific type using emplace.
See demo https://coliru.stacked-crooked.com/a/4dd054dc4fa9bb9a
My reading of the standard is that the code should be ill-formed due to ambiguity. It surprises me that both libstdc++ and libc++ appear to allow it.
Here's what [variant.ctor]/12 says:
Let T_j be a type that is determined as follows: build an imaginary function FUN(T_i) for each alternative type T_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t)) defines the alternative T_j which is the type of the contained value after construction.
So four functions are created: initially FUN(int), FUN(const int), FUN(volatile int), and FUN(const volatile int). These are all equivalent signatures, so they could not be overloaded with each other. This paragraph does not really specify what should happen if the overload set cannot actually be built. However, there is a note that strongly implies a particular interpretation:
[ Note:
variant<string, string> v("abc");
is ill-formed, as both alternative types have an equally viable constructor for the argument. —end note]
This note is basically saying that overload resolution cannot distinguish between string and string. In order for that to happen, overload resolution must be done even though the signatures are the same. The two FUN(string)s are not collapsed into a single function.
Note that overload resolution is allowed to consider overloads with identical signatures due to templates. For example:
template <class T> struct Id1 { using type = T; };
template <class T> struct Id2 { using type = T; };
template <class T> void f(typename Id1<T>::type x);
template <class T> void f(typename Id2<T>::type x);
// ...
f<int>(0); // ambiguous
Here, there are two identical signatures of f, and both are submitted to overload resolution but neither is better than the other.
Going back to the Standard's example, it seems that the prescription is to apply the overload resolution procedure even if some of the overloads could not be overloaded with each other as ordinary function declarations. (If you want, imagine that they are all instantiated from templates.) Then, if that overload resolution is ambiguous, the std::variant converting constructor call is ill-formed.
The note does not say that the variant<string, string> example was ill-formed because the type selected by overload resolution occurs twice in the list of alternatives. It says that the overload resolution itself was ambiguous (because the two types had equally viable constructors). This distinction is important. If this example were rejected after the overload resolution stage, an argument could be made that your code is well-formed since the top-level cv-qualifiers would be deleted from the parameter types, making all four overloads FUN(int) so that T_j = int. But since the note suggests a failure during overload resolution, that means your example is ambiguous (as the 4 signatures are equivalent) and this must be diagnosed.

Overloading conversion operator template

Consider the following simple example
struct C
{
template <typename T> operator T () {return 0.5;}
operator int () {return 1;}
operator bool () {return false;}
};
int main ()
{
C c;
double x = c;
std::cout << x << std::endl;
}
When compiled with Clang, it gives the following error
test.cpp:11:12: error: conversion from 'C' to 'double' is ambiguous
double x = c;
^ ~
test.cpp:4:5: note: candidate function
operator int () {return 1;}
^
test.cpp:5:5: note: candidate function
operator bool () {return false;}
^
test.cpp:3:27: note: candidate function [with T = double]
template <typename T> operator T () {return 0.5;}
^
1 error generated.
Other compilers generate similar errors, e.g., GCC and Intel iclc
If I remove operator int and operator bool. It compiles fine and work as expected. If only remove one of them, that is keep the template operator and say operator int, then the non-template version is always chosen.
My understanding is that only when the template and non-template overloaded functions are equal in the sense that they are both perfect match or both require the same conversion sequence, the non-template version will be preferred. However in this case, it appears that the compiler does not see the operator template as a perfect match. And when both the bool and int overloading are present, then naturally it considers they are ambiguous.
In summary, my question is that why the operator template is not considered a perfect match in this case?
Let's break this down into two different problems:
1. Why does this generate a compiler error?
struct C
{
operator bool () {return false;}
operator int () {return 1;}
};
As both int and bool can be implicitly converted to double, the compiler can not know which function it should use. There are two functions which it could use and neither one takes precedence over the other one.
2. Why isn't the templated version a perfect match?
struct C
{
template <typename T> operator T () {return 0.5;}
operator int () {return 1;}
};
Why is operator int() called when requesting a double?
The non-template function is called because a non-template function takes precedence in overload resolution. (Overloading function templates)
EDIT:
I was wrong! As Yan Zhou mentioned in his comment, and as it is stated in the link I provided, a perfect match in the templated function takes precedence over the non-templated function.
I tested your code (compiled with g++ 4.7.2), and it worked as expected: it returned 0.5, in other words, the templated function was used!
EDIT2:
I now tried with clang and I can reproduce the behaviour you described. As it works correctly in gcc, this seems to be a bug in clang.
This is interesting. There are two ways to read a critical part of section 13.3.3. The original example should definitely call the function template, but the version where one of the non-templates is removed might be argued to be ambiguous.
13.3.3:
A viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICS_i(F1) is not a worse conversion sequence than ICS_i(F2), and then
for some argument j, ICS_j(F1) is a better conversion sequence than ICS_j(F2), or, if not that,
the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type of F2 to the destination type, or, if not that,
F1 is a non-template function and F2 is a function template specialization, or, if not that,
F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.
If there is exactly one viable function that is a better function than all other viable functions, then it is the one selected by overload resolution; otherwise the call is ill-formed.
In the example, clang correctly identifies the set of three viable candidate functions:
C::operator int()
C::operator bool()
C::operator double<double>()
The third is a function template specialization. (I don't think the syntax above is legal, but you get the idea: at this point of overload resolution it's not treated as a template, but as a specialization with a definite function type.)
The only Implicit Conversion Sequence on arguments here (ICS1) is the exact match "lvalue C" to "C&" on the implicit parameter, so that won't make a difference.
This example is exactly the situation described in the second bullet, so the function returning double is clearly better than the other two.
Here's where it gets weird: By a very literal reading, operator int is also better than the template specialization, because of the third bullet. "Wait a minute, shouldn't 'better than' be antisymmetric? How can you say F1 is better than F2 AND F2 is better than F1?" Unfortunately, the Standard doesn't explicitly say anything of the sort. "Doesn't the second bullet take priority over the third bullet because of the 'if not that' phrase?" Yes, for constant F1 and F2. But the Standard doesn't say that satisfying the second bullet for (F1,F2) makes the third bullet for (F2,F1) not applicable.
Of course, since operator int is not better than operator bool and vice versa, there is still "exactly one viable function that is a better function than all other viable functions".
I'm not exactly endorsing this weird reading, except maybe to report it as a Standard defect. Going with that would have bizarre consequences (like removing an overload which was not the best from this example changes a program from well-formed to ambiguous!). I think the intent is for the second bullet to be considered both ways before the third bullet is considered at all.
Which would mean the function template should be selected by overload resolution, and this is a clang bug.

Global function template overloading and const parameters

If I compile (gcc 4.6.0) and run this code:
#include <iostream>
template <typename T> void F(/* const */ T& value) {
std::cout << "T & " << value << std::endl;
}
template <typename T> void F(/* const */ T* value) {
std::cout << "T * " << value << std::endl;
F(*value);
}
int main(int argc, char* argv[]) {
float f = 123.456;
float* pf = &f;
F(pf);
return 0;
}
I get the following output:
T * 0x7fff7b2652c4
T & 123.456
If I uncomment the const keywords I get the following output:
T & 0x7fff3162c68c
I can change float* pf = &f; to const float* pf = &f; to get the original output again, that's not the issue.
What I'd like to understand is why, when compiling with the const modifiers, overload resolution considers const T& value a better match than const T* valuefor a non-const float*?
During overload resolution, overloads requiring no conversions beat overloads requiring some conversions, even if those conversions are trivial. Quoting the C++03 standard, [over.match.best] (§13.3.3/1):
Define ICSi(F) as follows:
if F is a static member function, ICS1(F) is defined such that ICS1(F) is neither better nor worse than ICS1(G) for any function G, and, symmetrically, ICS1(G) is neither better nor worse than ICS1(F); otherwise,
let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F. 13.3.3.1 defines the implicit conversion sequences and 13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another.
Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
F1 is a non-template function and F2 is a function template specialization, or, if not that,
F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.5.2, or, if not that,
the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type of F2 to the destination type.
When const is present, in order to call the overload taking a reference, no conversion is necessary -- T is deduced to be float* and the argument is float* const&. However, in order to call the overload taking a pointer, float would need to be converted to float const for said overload to be viable. Consequently, the overload taking a reference wins.
Note, of course, that if pf were changed to be a float const*, the behavior would go back to the way you expected because the overload taking a pointer would no longer require a conversion.

Function with parameter type that has a copy-constructor with non-const ref chosen?

Some time ago I was confused by the following behavior of some code when I wanted to write a is_callable<F, Args...> trait. Overload resolution won't call functions accepting arguments by non-const ref, right? Why doesn't it reject in the following because the constructor wants a Test&? I expected it to take f(int)!
struct Test {
Test() { }
// I want Test not be copyable from rvalues!
Test(Test&) { }
// But it's convertible to int
operator int() { return 0; }
};
void f(int) { }
void f(Test) { }
struct WorksFine { };
struct Slurper { Slurper(WorksFine&) { } };
struct Eater { Eater(WorksFine) { } };
void g(Slurper) { }
void g(Eater) { } // chooses this, as expected
int main() {
// Error, why?
f(Test());
// But this works, why?
g(WorksFine());
}
Error message is
m.cpp: In function 'int main()':
m.cpp:33:11: error: no matching function for call to 'Test::Test(Test)'
m.cpp:5:3: note: candidates are: Test::Test(Test&)
m.cpp:2:3: note: Test::Test()
m.cpp:33:11: error: initializing argument 1 of 'void f(Test)'
Can you please explain why one works but the other doesn't?
Overload resolution picks the function that is the closest match to the supplied argument. You supplied a Test. No conversion necessary -- identity conversion used. Thus function resolution chooses f(Test). Test can't be copied from rvalue, which you supplied, but overload resolution has succeeded already...conversion to int is never checked.
g(Eater) is chosen because the types don't exactly match, the identity conversion is NOT used, and the compiler has to find a conversion routine that works. g(Slurper) doesn't because you can't make one out of the supplied argument.
"Why doesn't this one fail: struct A { operator int(); }; void f(A&); void f(int); void g() { f(A()); }"
Because f(A&) is not a viable overload for the supplied argument. In this case the parameter is a reference and the fact that temps don't bind to non-const is allowed to effect the resolution. In this case it does and the that version of the function becomes a non-candidate, leaving only the one and it works.
Basically, for overload resolution, it is assumed that an object of type A can be converted to an object of type A regardless of any cv-qualification on either of the two.
From draft n1905:
13.3.3.1: Overloading.Overload Resolution.Best Viable Function.Implicit Conversion Sequences
6 When the parameter type is not a reference, the implicit conversion sequence models a copy-initialization of the parameter from the argument expression. The implicit conversion sequence is the one required to convert the argument
expression to an rvalue of the type of the parameter. [ Note: when the parameter has a class type, this is a conceptual
conversion defined for the purposes of clause 13; the actual initialization is defined in terms of constructors and is not
a conversion. — end note ] Any difference in top-level cv-qualification is subsumed by the initialization itself and does
not constitute a conversion. [ Example: a parameter of type A can be initialized from an argument of type const A.
The implicit conversion sequence for that case is the identity sequence; it contains no “conversion” from const A to
A. — end example ] When the parameter has a class type and the argument expression has the same type, the implicit
conversion sequence is an identity conversion. [...]