C++ priority of compare operator overload vs conversion operator - c++

Consider the following program:
#include <iostream>
using namespace std;
class Foo {
public:
int k;
operator int() {
cout << "convert int" << endl;
return k;
}
#if USE_COMPARE
bool operator < (int rhs) {
cout << "compare with" << endl;
return (k < rhs);
}
#endif
};
int main()
{
Foo f;
f.k = 3;
int m = 5;
if (f < m) {
cout << 1 << endl;
return 1;
}
cout << 0 << endl;
return 0;
}
When USE_COMPARE is defined, the comparison of if (f<m) will use the compare operator overload. If USE_COMPARE is not defined, it will convert f from Foo to int, and then do the integer compare. It seems to me that the compare operator overload is in higher priority than the conversion operator. Could any one confirm this in the C++ standard point of view?
Yet I think it's nature that the compare operator should take priority. But please answer the question in the perspective of C++ standard.
Thanks.

13.3.3.2/2
When comparing the basic forms of implicit conversion sequences (as
defined in 13.3.3.1)
a standard conversion sequence (13.3.3.1.1) is a better conversion
sequence than a user-defined con- version sequence or an ellipsis
conversion sequence, and
a user-defined conversion sequence (13.3.3.1.2) is a better conversion
sequence than an ellipsis conver- sion sequence (13.3.3.1.3).
13.3.3.1/3
A well-formed implicit conversion sequence is one of the following
forms: — a standard conversion sequence (13.3.3.1.1),
— a user-defined conversion sequence (13.3.3.1.2), or
— an ellipsis conversion sequence (13.3.3.1.3).
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).
13.3.3.1.2/1
A user-defined conversion sequence consists of an initial standard
conversion sequence followed by a user- defined conversion (12.3)
followed by a second standard conversion sequence. If the user-defined
conversion is specified by a conversion function (12.3.2), the initial
standard conversion sequence converts the source type to the implicit
object parameter of the conversion function.
There are two variants for compiler, if compare operator is defined:
1) Let
IF = Identity(f)
Call:
IF.operator <(int)
2) Let:
IF = Identity(f);
converted_int = Identity(IF.operator int());
Call:
operator < (converted_int, int);
Implicit-conversion sequence is better than user-conversion sequence.
There are two many words in standard about overload resolution for quotes, if you want you can read par 13.3, or just 13.3.3[over.best.ics].

Related

Overloaded function and multiple conversion operators ambiguity in C++, compilers disagree

In the following program struct S provides two conversion operators: in double and in long long int. Then an object of type S is passed to a function f, overloaded for float and double:
struct S {
operator double() { return 3; }
operator long long int() { return 4; }
};
void f( double ) {}
void f( float ) {}
int main() {
S s;
f( s );
}
MSVC compiler accepts the program fine, selecting f( double ) overload. However both GCC and Clang see an ambiguity in the calling of f, demo: https://gcc.godbolt.org/z/5csd5dfYz
It seems that MSVC is right here, because the conversion:
operator long long int() -> f( float ) is not a promotion. Is it wrong?
There is a similar question Overload resolution with multiple functions and multiple conversion operators, but there is a promotion case in it and all compilers agree now, unlike the case in this question.
GCC and Clang are correct. The implicit conversion sequences (user-defined conversion sequences) are indistinguishable.
[over.ics.rank]/3:
(emphasis mine)
Two implicit conversion sequences of the same form are
indistinguishable conversion sequences unless one of the following
rules applies:
...
(3.3) User-defined conversion sequence U1 is a better conversion sequence
than another user-defined conversion sequence U2 if they contain the
same user-defined conversion function or constructor or they
initialize the same class in an aggregate initialization and in either
case the second standard conversion sequence of U1 is better than the
second standard conversion sequence of U2.
The user-defined conversion sequences involves two different user-defined conversion functions (operator double() and operator long long int()), so compilers can't select one; the 2nd standard conversion sequence won't be considered.

Does C++ guarantee this enum vs int constructor overload resolution?

Consider this example program:
#include <iostream>
typedef enum { A, B, C } MyEnum;
struct S
{
S(int) { std::cout << "int" << std::endl; }
S(MyEnum) { std::cout << "MyEnum" << std::endl; }
};
S f()
{
return A;
}
int main()
{
S const s = f();
}
Compiled with both clang and gcc this produces an executable that prints "MyEnum" when run. Is this behavior guaranteed by the C++ standard?
Yes, S::S(MyEnum) wins in overload resolution because it's an exact match. While S::S(int) requires one more implicit conversion (integral promotion) from enum to int.
Each type of standard conversion sequence is assigned one of three ranks:
Exact match: no conversion required, lvalue-to-rvalue conversion, qualification > conversion, function pointer conversion, (since C++17) user-defined conversion of class type to the same class
Promotion: integral promotion, floating-point promotion
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
A standard conversion sequence S1 is better than a standard conversion sequence S2 if
a) S1 is a subsequence of S2, excluding lvalue transformations. The identity conversion sequence is considered a subsequence of any other conversion
b) Or, if not that, the rank of S1 is better than the rank of S2
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
there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2
These pair-wise comparisons are applied to all viable functions. If exactly one viable function is better than all others, overload resolution succeeds and this function is called. Otherwise, compilation fails.
Yes, of course. return statement allows implicit construction and S(MyEnum) is the exact match.
Same would work with return {A};
But if you were to make S(MyEnum) explicit, then:
return A; will call S(int) as a fallback because MyEnum is implicitly convertible to integers. But this is worse overload candidate than S(MyEnum) due to the extra conversion, chosen only from necessity.
return {A}; represents copy-list initialization. It will fail because it forbids explicit constructors and implicit conversions.
return S{A}; represents direct-list initialization, it will call S(MyEnum), although it limits some implicit conversion, it does not impact this example and S(int) would be called had S(MyEnum) was removed.
return S(A); is essentially the same as return A; given the specified return type S.

C++ function overload priority [duplicate]

This question already has answers here:
Unexpected overload resolution in visual studio involving void*, string and const char[]
(3 answers)
Closed 7 years ago.
Why would the code give an output: bool ?
Is there any way I could make the const char* to match the string version?
#include <string>
#include <iostream>
void func(bool)
{
std::cout << "bool" << std::endl;
}
void func(const std::string&)
{
std::cout << "string" << std::endl;
}
int main(int argc, char* argv[])
{
func("hello");
}
This happens because the compiler will prefer built-in conversions to user-defined conversions. The conversion from a pointer to a bool is built-in, so that overload is selected rather than constructing a std::string.
You could add an overload which takes a const char* and forwards it to the std::string version:
void func(const char* arg)
{
func(std::string{arg});
}
To answer the why:
Function matching is the process by which the compiler selects which function to call among an overload set.
Here, there are two viable candidates (the two functions you defined). To pick one, the compiler ranks the conversion they imply.
The first candidate void func(bool) implies an array-to-pointer conversion followed by a boolean conversion (from const char[6] to const char* to bool)
The second candidate implies a user-defined conversion (calling the std::string ctor taking a const char*)
The second conversion has lower ranking, so the first candidate is selected as the best match.
According to the standard N4431 §13.3.3.2/2 Ranking implicit conversion sequences [over.ics.rank] (emphasis mine):
When comparing the basic forms of implicit conversion sequences (as
defined in 13.3.3.1) (2.1) — a standard conversion sequence
(13.3.3.1.1) is a better conversion sequence than a user-defined
conversion sequence or an ellipsis conversion sequence, and (2.2) — a
user-defined conversion sequence (13.3.3.1.2) is a better conversion
sequence than an ellipsis conversion sequence (13.3.3.1.3).
Consequently, because the char const * to bool is a standard implicit conversion compared to the implicit conversion to std::string which is a user-defined conversion is a better conversion and is preferred in overload resolution.
In order to force overload resolution to choose the std::string version:
func(std::string("hello"));

Why no ambiguity in this function call?

I'm wondering why there's no ambiguity in this function call:
#include <iostream>
#include <vector>
template <class T>
class C
{
public:
typedef char c;
typedef double d;
int fun() {}
static c testFun( decltype(&C::fun) ) {return c();}
static d testFun(...) { return d(); }
};
int main() {
C<int>::testFun(0); // Why no ambiguity?
}
http://coliru.stacked-crooked.com/a/241ce5ab82b4a018
There's a ranking of implicit conversion sequences, as defined in [over.ics.rank], emphasis mine:
When comparing the basic forms of implicit conversion sequences...
- a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion
sequence or an ellipsis conversion sequence, and
- a user-defined conversion sequence (13.3.3.1.2) is a better conversion sequence than an ellipsis conversion
sequence (13.3.3.1.3).
So we have two functions:
static char testFun( int (C::*)() ) { return char(); }
static double testFun( ... ) { return double(); }
Both functions are viable for testFun(0). The first would involve a "null member pointer conversion" as per [conv.mem], and is a standard conversion sequence. The second would match the ellipsis and be an ellipsis conversion sequence. By [over.ics.rank], the former is preferred. There's no ambiguity, the one is strictly better than the other.
An ambiguous overload would arise if we had two equivalent conversion sequences that the compiler could not decide between. Consider if we had something like:
static char testFun(int* ) { return 0; }
static int testFun(char* ) { return 0; }
testFun(0);
Now both overloads would be equivalent as far as the conversion sequences go, so we'd have two viable candidates.
You have a standard conversion vs an ellipsis conversion. The standard says that a standard conversion is a better conversion sequence than the latter. [over.ics.rank]/p2:
a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion
sequence or an ellipsis conversion sequence
A pointer-to-member conversion is a standard conversion sequence. 0 is a null pointer constant and can be converted to a pointer-to-member. [conv.mem]/p1:
A null pointer constant (4.10) can be converted to a pointer to member type; the result is the null member
pointer value of that type and is distinguishable from any pointer to member not created from a null pointer
constant. Such a conversion is called a null member pointer conversion.
Therefore the first overload is preferred.
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).
And also
Viable functions
Given the set of candidate functions, constructed as described above, the next step of overload resolution is examining arguments and parameters to reduce the set to the set of viable functions
To be included in the set of viable functions, the candidate function must satisfy the following:
1) If there are M arguments, the candidate function that has exactly M parameters is viable
2) If the candidate function has less than M parameters, but has an ellipsis parameter, it is viable.
[...]
Overloading resolution
A null pointer literal 0 should be an exact match to a function accepting a function pointer and treated as stronger than matching anything (...).

Precedence of overloaded cast operators

In the code below, I would expect to get a compiler error if more than one cast operator is defined because of the ambiguity.
#include <iostream>
#include <sstream>
struct A
{
operator const char*() { return "hello world\n"; }
operator float() { return 123.0F; }
//operator int() { return 49; }
};
int main()
{
A a;
std::stringstream ss;
ss << a;
std::cout << ss.str();
return 0;
}
Instead, as long as only one numeric cast operator is defined then it compiles with no errors, no warnings, and the numeric cast is used in preference to the operator const char *(). The order of the declared operators makes no difference.
However if operator int() and operator float() are both defined then I get what I expected from the start:
'operator <<' is ambiguous
Are there precedence rules for casts, or why does the compiler choose the numeric cast by default? I do understand that I should explicitly state which cast I mean, but my question is on the default choice the compiler makes.
Edit: Using compiler MSVC 2010
Conversions are ranked according to § 13.3.3.1 of the C++ Standard. In particular, user-defined conversion sequences pertinent to your example are regulated by § 13.3.3.1.2/1:
"A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion (12.3) followed by a second standard conversion sequence. [...] If the user-defined conversion is specified by a conversion function (12.3.2), the initial standard conversion sequence converts the source type to the implicit object parameter of the conversion function."
All conversions sequences here involve:
a fictitious conversion to the source type of the implicit object parameter of the conversion function;
a user-defined conversion;
an identity conversion to the input type of operator <<.
These conversion sequences all have the same rank. Thus, the call should be ambiguous. If it is not, for me it is a compiler bug.