Overloading resolution - c++

As far as I know, when coming to choose between two candidate functions the compiler will prefer the one that its weakest match is stronger.
For example if I have:
void boo(int i, char c);
void boo(double d, int i);
for the following code:
float f = 1.0;
char c = 'c';
boo(f,c);
the second boo should be prefered because its weakest match is promotion while the first one's is standard type conversion.
But when I try to compile it (using gcc), I get:
error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second.
Any ideas?

Your understanding of overload resolution is wrong. The general
rule (when there are more than one argument) is to choose
a function for which at least one argument is better (it doesn't
matter how much better), and none of the others are worse. In
other words, the compiler processes each argument separately,
creating a set of "best matches" for it. After this, it takes
the union of these sets: if the intersection contains exactly one
function, you've won. Otherwise, it's ambiguous.

Let's say that you called the function f.
I think that the overload resolution process is:
1. Creating a set of candidate functions. This set of functions includes all of the functions named f that can be accessed from the point where you called f().
2. Creating a set of viable functions. This set of functions is a subset of the candidate functions for which the number of parameters of each viable function agrees with the number of arguments you used to call f().
3. Chooses the best viable function. The best viable function is the one whose parameters all have either better or equal-ranked implicit conversion sequences than all of the other viable functions.
If there are more or less than one (not exactly one), there will be a compilation error.
The following example demonstrates it nicely:
class cat
{
public:
cat(int);
};
void func(int, int, int, cat)
{
std::cout << 1 << std::endl;
}
void func(int, int, double, double)
{
std::cout << 2 << std::endl;
}
int main()
{
func(1,2,3,4);
}
This code generates a compilation error (VS) or warning (g++).
The following will run correctly (print 1):
void func(int, int, int, double)
{
std::cout << 1 << std::endl;
}
void func(int, double, double, double)
{
std::cout << 2 << std::endl;
}
int main()
{
func(1,2,3,4);
}

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.

Resolution of function overloading about initializer_list

#include <vector>
using namespace std;
class A
{
public:
explicit A(const initializer_list<int> & a) {}
};
void func(const vector<int>& a)
{
}
void func(A a)
{
}
int main(void)
{
func({ 1,2,3 });
}
This code fails to compile:
(19): error C2668: 'func': ambiguous call to overloaded function
(13): note: could be 'void func(A)'
(9): note: or 'void func(const std::vector> &)'
with[_Ty=int]
(19): note: while trying to match the argument list '(initializer list)'
Note that I specified 'explicit' on A's constructor.
In my view, func(A a) should not be considered as a candidate of {1,2,3}. And actually, it is not. If I remove func(const vector<int>& a), then the code still fails, instead of succeeding by removing ambiguity.
In summary, in this code, the func(const vector<int>& a) is the only callable function for {1,2,3}, so there is no ambiguity.
My question is..
How does C++ overloading resolution procedures come to conclusion of 'ambiguous'?
Why doesn't C++ just simply choose callable one?
explicit constructors are not ignored when you perform list initialization. Such constructors are always considered as viable overload candidates. What happens is, if the system attempts to call an explicit constructor under copy-list-initialization (ie: after overload resolution), then you get a hard compile error.
In your case, it never gets that far because the overload set is ambiguous.
explicit doesn't mean "doesn't exist if you try to convert"; it means "error if you try to convert". The point of explicit is to force the user to think about what type they actually want to use. It's there to prevent a user from writing code that is somewhat ambiguous to the reader.
I believe clang is correct here. Overload resolution in C++ works in three phases: First a set of candidate functions is constructed, which is the set of all functions that the call may potentially refer to (basically, the set of all functions that name resolution picks up). This initial set of candidate functions is then narrowed down to arrive at a set of viable functions (the set of functions that could take a call with the given arguments). Finally, the viable functions are ranked to determine the best viable function. This best viable function is what ultimately will be called.
From [over.match.viable]/4
Third, for F to be a viable function, there shall exist for each argument an implicit conversion sequence that converts that argument to the corresponding parameter of F. […]
Based on [over.best.ics]/6, particularly
When the parameter type is not a reference, the implicit conversion sequence models a copy-initialization of the parameter from the argument expression. […]
it would seem that there is no such implicit conversion sequence for void func(A a) due to the necessary constructor being marked explicit (copy-initialization would fail). Therefore, the function is not a viable function and is not considered anymore for overload resolution, which leaves void func(const vector<int>& a) as the only viable candidate, which is the function that will then be called.
Also, purely on a conceptional level, it would seem to make sense that the copy-list-initialization of a parameter can only be ill-formed once we actually know which parameter we're going to initialize, i.e., know which function is actually going to be called. If a call to an overload set would be illegal the moment there is a single argument that is not a valid initializer for the corresponding parameter in every single potential candidate function, then what's the point of overloading? As long as we're still working on figuring out which function to call, there is no way to decide whether the initialization would be ill-formed or not. clang exhibits exactly this behavior. When you comment out the void func(const std::vector<int>& a) overload, clang will suddenly complain that the call is ill-formed…
try it out here
I agree with #Nicol Bolas. MSVC and gcc are right while clang and icc are wrong.
In overloading resolution, list initialization differs from copy initialization that list initialization considers explicit constructors while copy initialization doesn't.
(From cppreference)
List-initialization When an object of non-aggregate class type T is
list-initialized, two-phase overload resolution takes place.
at phase 1, the candidate functions are all initializer-list
constructors of T and the argument list for the purpose of overload
resolution consists of a single initializer list argument if overload
resolution fails at phase 1, phase 2 is entered, where the candidate
functions are all constructors of T and the argument list for the
purpose of overload resolution consists of the individual elements of
the initializer list. If the initializer list is empty and T has a
default constructor, phase 1 is skipped.
In copy-list-initialization, if phase 2 selects an explicit
constructor, the initialization is ill-formed (as opposed to all over
copy-initializations where explicit constructors are not even
considered).
Some examples:
This one
#include <iostream>
#include <initializer_list>
struct A
{
explicit A(int, int, int) {}
};
struct B
{
B(std::initializer_list<int>) {}
};
void f(A) //f1
{
std::cout << 1 << std::endl;
}
void f(B) //f2
{
std::cout << 2 << std::endl;
}
int main()
{
f({ 1,2,3 }); //list initialziation
}
fails on MSVC and gcc. (See here and here)
This one
#include <iostream>
#include <initializer_list>
struct A
{
explicit A(std::initializer_list<int>) {}
};
struct B
{
B(std::initializer_list<int>) {}
};
void f(A) //f1
{
std::cout << 1 << std::endl;
}
void f(B) //f2
{
std::cout << 2 << std::endl;
}
int main()
{
f({ 1,2,3 }); //Also list initialization
}
also fails on MSVC and gcc. (See here and here)
While this one
#include <iostream>
#include <initializer_list>
struct A
{
explicit A(int) {}
};
struct B
{
B(int) {}
};
void f(A) //f1
{
std::cout << 1 << std::endl;
}
void f(B) //f2
{
std::cout << 2 << std::endl;
}
int main()
{
f(1); //Copy initialization
}
successes on all four compilers.

Function overload resolution with nullptr as argument

Consider the code below. Although both overloads of fun accept pointers, passing nullptr to fun does not result in any compilation error. Whereas, the very similar function bun fails to compile. When I print the the types of the argument i using typeid(i).name() (after modifying the code just to get this printed) I get the same type, simply int*. What is the rule that resolves the ambiguity in fun case, but fails for bun? Thanks in advance!
#include <iostream>
struct Foo {
int sth;
};
template< class U>
void fun(decltype(U::sth)* i){
std::cout << "1" << std::endl;
}
template< class U>
void fun(U* i){
std::cout << "2" << std::endl;
}
void bun(decltype(Foo::sth)* i){
std::cout << "3" << std::endl;
}
void bun(Foo* i){
std::cout << "4" << std::endl;
}
int main ( )
{
fun<Foo>(nullptr);
// bun(nullptr); --> call of overloaded 'bun(std::nullptr_t)' is ambiguous
return 0;
}
-----------------------
output : 1
Well, in fact, GCC accepts your code, but Clang does not. So it is not, at first, obvious whether the call is ambiguous.
You ask what rule resolves the ambiguity in the fun case; GCC evidently thinks that there is such a rule. I imagine the rule that GCC is applying is the rule [over.match.best]/1.7 that prefers a more specialized function template over a less specialized one.
The procedure for determining which function template is more specialized than the other is described in [temp.func.order] and is explained thoroughly in this SO answer. However, you will notice that when attempting to apply this procedure to the two overloads of fun as in this question, we run into the problem that the unique synthesized type that needs to be substituted for U in the first overload would need to have a member named sth, and the nature of this member is not specified, and although it may be clear to a human that deduction in the second fun overload must succeed regardless of what sth's type is, the compiler may not be able to prove it.
This is CWG 1157. As this issue is still open with no proposed resolution, I have no insight into whether WG21 intends for this overload resolution to succeed or not.

Default argument promotion in functions

I'm trying to see default argument promotion in functions. Specifically I want to test section 6.5.2.2 Function calls (described here).
I want to have a prototype-less function call to see default argument promotion to integer but I get "Function does not take 1 arguments" error. This is what I'm trying to do:
#include<iostream>
using namespace std;
//void Func(char val);
//void Func(int val);
void Func(); // No prototype
int main(int argc, char** argv)
{
char charVal = 'a';
cout << "Func(charVal) - "; Func(charVal);
return 0;
}
void Func(char val)
{
cout << "Char arg. Result: " << val << endl;
}
void Func(int val)
{
cout << "Int arg. Result: " << val << endl;
}
I expected to see Func(int) being called due to argument promotion.
Is this removed from the standard already?
Cheers.
P.S- I just saw that this kind of prototype-less declarations are part of C standard, NOT C++. Any particular reason why C++ doesn't support it?
All functions (and named entities in general) need to be declared before use. You've only declared the overload with no parameters (which is what an empty parameter list means in C++) when you try to call it with an argument in main.
You have the correct declarations at the start of the file, but for some reason the one you need is commented out. Uncomment them and it's fine.
I just saw that this kind of prototype-less declarations are part of C standard, NOT C++. Any particular reason why C++ doesn't support it?
Because C++ supports overloading. Overload resolution happens at compile time, where the function is called, and candidates can only be considered if the compiler knows they exist - that is, if they've been fully declared so that the compiler can match their signatures against the argument types of the function call.
In C, the compiler knows which function you mean, whether or not it knows the parameter types, since it's the only one with that name. Such declarations are merely dangerous, removing type-checking from the argument types and opening the door to all manner of bugs.
C++ has never supported prototype-less function declarations. The empty argument list in C++ means no arguments, not a lack of prototype.
The reason is that C++ has type-safe linking, and that just doesn't work if you don't have a prototype. And C++ has type-safe linking because that's just a good idea and reduces bugs. (You can read Design&Evolution of C++ for more details.)
Argument promotions in C++ exist only in calls to true variadic functions, i.e. those declared with a ... in the argument list.