Reference binding through ambiguous conversion operator - c++

#include <iostream>
using namespace std;
struct CL2
{
CL2(){}
CL2(const CL2&){}
};
CL2 cl2;
struct CL1
{
CL1(){}
operator CL2&(){cout<<"operator CL2&"; return cl2;}
operator const CL2&(){cout<<"operator const CL2&"; return cl2;}
};
CL1 cl1;
int main()
{
CL1 cl1;
CL2 cl2 (cl1);
}
Both clang and gcc give ambiguous conversion operator, but Visual Studio compiles ok and prints "operator const CL2&". How must be right according to Standard?
As I undestand, conversion of CL1 to const CL2& is in copy-initialization context (as a part of a direct-initialization of cl2 object). I seen n4296 draft, [over.match.copy]:
Assuming that “cv1 T” is the type of the object being initialized,
with T a class type, the candidate functions are selected as follows:
— The converting constructors (12.3.1) of T are candidate
functions.
— When the type of the initializer expression is a
class type “cv S”, the non-explicit conversion functions of S and its
base classes are considered. When initializing a temporary to be bound
to the first parameter of a constructor where the parameter is of type
“reference to possibly cv-qualified T” and the constructor is called
with a single argument in the context of direct-initialization of an
object of type “cv2 T”, explicit conversion functions are also
considered. Those that are not hidden within S and yield a type whose
cv-unqualified version is the same type as T or is a derived class
thereof are candidate functions. Conversion functions that return
“reference to X” return lvalues or xvalues, depending on the type of
reference, of type X and are therefore considered to yield X for this
process of selecting candidate functions.
I.e. both of conversion operators are considered as return CL2 and const CL2 (not just CL2 without const) and it remains to solve, which conversion is better: CL2 -> const CL2& or const CL2 -> const CL2&. The second case seems more appropriate. Should a better qualification conversion considered in that context? Or both cases are Identity conversion? I couldn't find it in Standard

Since both conversion operators have identical signatures, the only way in which one could be preferred over the other is by application of [over.match.best]/(1.4)…
— 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 (1.5):
— the context is an initialization by conversion function for direct
reference binding (13.3.1.6) of a reference to function type, […]
Clearly, neither applies, hence the ambiguity. A possible way to disambiguate:
operator CL2&();
operator const CL2&() const;
Demo; Here, the former overload's initial standard conversion sequence of the implicit object argument is better as per [over.ics.rank]/(3.2.6), which is decisive by [over.match.best]/(1.3).

Related

Which of two conversion operators must be selected by C++ compiler?

A class can declare several conversion operators. In particular it can be conversion operators to some type and to const-reference of the same type. Which of the two conversion operators must be selected in case of requested conversion to that type?
Consider an example:
#include <iostream>
struct B {};
static B sb;
struct A {
operator B() { std::cout << "operator B() "; return sb; }
operator const B &() { std::cout << "operator const B &() "; return sb; }
};
int main() {
A a;
[[maybe_unused]] B b(a);
}
Here Clang selects operator B(), MSVC selects operator const B &(), and GCC complains about ambiguity of the selection:
<source>:13:27: error: call of overloaded 'B(A&)' is ambiguous
13 | [[maybe_unused]] B b(a);
| ^
<source>:3:8: note: candidate: 'constexpr B::B(const B&)'
3 | struct B {};
| ^
<source>:3:8: note: candidate: 'constexpr B::B(B&&)'
Demo: https://gcc.godbolt.org/z/874h7h3d1
Which of the compilers is right?
The program is ill-formed and rejected by GCC is correct here but the diagnosis can arguably say it is not completely correct. For this declaration B b(a);, it is direct-initialization of an object of class B from the initializer a of type A, according to [over.match.copy] p1
Assuming that “cv1 T” is the type of the object being initialized, with T a class type, the candidate functions are selected as follows:
The converting constructors of T are candidate functions.
When the type of the initializer expression is a class type “cv S”, conversion functions are considered. The permissible types for non-explicit conversion functions are T and any class derived from T. When initializing a temporary object ([class.mem]) to be bound to the first parameter of a constructor where the parameter is of type “reference to cv2 T” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv3 T”, the permissible types for explicit conversion functions are the same; otherwise there are none.
For converting constructors, they are copy/move constructors of B, however, [over.best.ics#general-4] prohibits the user-defined conversion sequence to apply to the target to match the parameter of the constructor
However, if the target is
the first parameter of a constructor or
[...]
and the constructor or user-defined conversion function is a candidate by
[...]
[over.match.copy], [over.match.conv], or [over.match.ref] (in all cases), or
[...]
user-defined conversion sequences are not considered.
Hence, the copy/move constructors of B are not viable functions. The ambiguity arises from the viable functions A::operator B() and A::operator const B &(), since the implicit parameter objects of them both have type A& and the corresponding argument is an lvalue of type A, hence neither is better than the other. Hence, the only opportunity that can determine which is better falls on [over.match.best#general-2.2]
the context is an initialization by user-defined conversion (see [dcl.init], [over.match.conv], and [over.match.ref]) 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.
The second standard conversion sequences of them are both identity conversions, hence they are not indistinguishable. So, the result is ambiguity. GCC is merely correct in that the program is ambiguous, but, obviously, its diagnosis has a bit misleading. Since the copy/move constructors are not viable functions in this case at all, how could they cause the ambiguity? If we suppress the production of the defaulted move constructor, GCC and Clang are both incorrect here, which is back to this question you have referred.

why the explicit conversion function of derived class return type is not a candidate in the context of direct-initialization

Given the following example:
#include <iostream>
struct A{
A() = default;
A(A const&){}
};
struct B:A{};
struct C{
explicit operator B(){
return B{};
}
};
int main(){
C c;
A a(c); // #1
}
GCC and Clang both report that c cannot be converted to const A&. However, there's a relevant rule in the standard says, explicit operator B() should be a candidate function in this context. That is:
over.match.copy#1.2
When initializing a temporary to be bound to the first parameter of a constructor where the parameter is of type “reference to possibly cv-qualified T” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv2 T”, explicit conversion functions are also considered. Those that are not hidden within S and yield a type whose cv-unqualified version is the same type as T or is a derived class thereof are candidate functions.
Moreover, in the current standard, the relevant rule still has the same meaning:
over.match.copy#1.2
When the type of the initializer expression is a class type “cv S”, conversion functions are considered. The permissible types for non-explicit conversion functions are T and any class derived from T. When initializing a temporary object ([class.mem]) to be bound to the first parameter of a constructor where the parameter is of type “reference to cv2 T” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv3 T”, the permissible types for explicit conversion functions are the same; otherwise there are none.
Regardless of c++17 standard or the current standard, they all say that in the context likes #1, the explicit conversion function that has the return type of class T or any class derived from T is also a candidate.
In my example, The object a of type A is direct-initialized by the single initializer c, which is the single argument in the invocation of constructor, the copy constructor of A has a parameter A const&. In this case, c should be used to initialize a temporary object to be bound to the first parameter. Hence, in this case explicit operator B() should be considered as a candidate function. However, GCC and Clang both reject this example. GCC obviously report a dignosis information that make no sense.
Is it a bug of these compilers? Or, I misunderstand something?

Implicit conversion with multiple intermediate conversion steps [duplicate]

class C {
public:
C() { }
};
class B {
public:
B(C c) { }
B() { }
};
class A {
public:
A(bool b) { }
A(B b) { }
};
int main() {
A a1 = true; // bool -> A is allowed
A a2 = B(); // B -> A is allowed
A a3 = 7; // int -> bool -> A is allowed
A a4 = C(); // C -> B -> A isn't allowed
}
Why I can use two-step implicit conversion with bool but can't use it with C?
What is the general rule describing multistep implicit conversion?
There is no multi-step user-defined implicit conversion.
int -> bool -> A
is allowed because the int->bool conversion isn't user-defined.
12.3 Conversions [class.conv]
1 Type conversions of class objects can be specified by constructors
and by conversion functions. These conversions are called user-defined
conversions and are used for implicit type conversions (clause 4), for
initialization (8.5), and for explicit type conversions (5.4, 5.2.9).
2 User-defined conversions are applied only where they are unambiguous
(10.2, 12.3.2). Conversions obey the access control rules (clause 11).
Access control is applied after ambiguity resolution (3.4).
3 [ Note:
See 13.3 for a discussion of the use of conversions in function calls
as well as examples below. —end note ]
4 At most one user-defined
conversion (constructor or conversion function) is implicitly applied
to a single value.
Since this construction is perfectly legal
A a4((C()));
problem is, that you use copy initization. Really, your example is equal to
A a4((A(C()));
8.5/16
The semantics of initializers are as follows. The destination type is the type of the object or reference being
initialized and the source type is the type of the initializer expression. If the initializer is not a single (possibly
parenthesized) expression, the source type is not defined.
If the destination type is a (possibly cv-qualified) class type:
— Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences
that can convert from the source type to the destination type or (when a conversion function
is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is
chosen through overload resolution (13.3).
13.3.1.4/1
Under the conditions specified in 8.5, as part of a copy-initialization of an object of class type, a user-defined
conversion can be invoked to convert an initializer expression to the type of the object being initialized.
Overload resolution is used to select the user-defined conversion to be invoked. Assuming that “cv1 T” is
the type of the object being initialized, with T a class type, the candidate functions are selected as follows:
— The converting constructors (12.3.1) of T are candidate functions.
— When the type of the initializer expression is a class type “cv S”, the non-explicit conversion functions of
S and its base classes are considered.
13.3.3.1/4
However, when considering the argument of a constructor or user-defined conversion function that is a
candidate by 13.3.1.3 when invoked for the copying/moving of the temporary in the second step of a class
copy-initialization, by 13.3.1.7 when passing the initializer list as a single argument or when the initializer
list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is
considered for the first parameter of a constructor of X, or by 13.3.1.4, 13.3.1.5, or 13.3.1.6 in all cases, only
standard conversion sequences and ellipsis conversion sequences are considered.
Your user-defined conversion (C -> B) is not considered in this case.

Should initialization by conversion function be ambiguous when two candidates have the same cv-qualification?

Both clang and gcc accept the following code and choose A::operator B*.
struct B
{
};
struct A : B
{
operator A*();
operator B*();
};
A a;
void* x = a;
My reading of the standard - specifically sentences highlighted below in bold - suggests that this conversion should be ambiguous.
Both A::operator A* and A::operator B* are candidates for overload resolution because A* and B* are both convertible to void* via a standard conversion. Because the implied object parameter A& is the only argument, only the conversion sequence that converts from the implied object argument to the implied object parameter is considered - the type yielded by the conversion function is ignored. In both cases, the implied object argument is the initializer expression's type A, and the implied object parameter is A&. If both conversion sequences are identical, there is no way to distinguish between the two candidates.
8.5 Initializers [dcl.init]
The semantics of initializers are as follows. The destination type is the type of the object or reference being
initialized and the source type is the type of the initializer expression.
— If the destination type is a [reference/array/class...] [deleted details not applicable to this scenario]
— Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered.
The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload
resolution (13.3). The user-defined conversion so selected is called to convert the initializer
expression into the object being initialized. If the conversion cannot be done or is ambiguous, the
initialization is ill-formed.
13.3.1.5 Initialization by conversion function [over.match.conv]
Under the conditions specified in 8.5, as part of an initialization of an object of nonclass type, a conversion
function can be invoked to convert an initializer expression of class type to the type of the object being
initialized. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1
T” is the type of the object being initialized, and “cv S” is the type of the initializer expression, with S a
class type, the candidate functions are selected as follows:
— The conversion functions of S and its base classes are considered. Those non-explicit conversion
functions that are not hidden within S and yield type T or a type that can be converted to type T
via a standard conversion sequence (13.3.3.1.1) are candidate functions. For direct-initialization, those
explicit conversion functions that are not hidden within S and yield type T or a type that can be
converted to type T with a qualification conversion (4.4) are also candidate functions. Conversion
functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type
for this process of selecting candidate functions. Conversion functions that return “reference to cv2
X” return lvalues or xvalues, depending on the type of reference, of type “cv2 X” and are therefore
considered to yield X for this process of selecting candidate functions.
The argument list has one argument, which is the initializer expression. [ Note: This argument will be
compared against the implicit object parameter of the conversion functions. —end note ]
Is this ambiguous according to the standard?
EDIT: note that this is a similar question, but not the same as Distinguishing between user-defined conversion sequences by the initial standard conversion sequence
The difference being that in my example both conversion functions have the same qualification.
TLDR: When everything else is equal, overload resolution breaks the tie by which conversion function has the best conversion from its return value to the target type.
All references are to ISO/IEC 14882:2011 (C++11). The behavior of the initialization:
void* x = a;
is defined as follows. First, this is an initialization as described in 8.5 Initializers [dcl.init] and conforms to the grammar described in p1. Since the destination type void* is a non-class type, and the source type A is a class type, this specific initializer is of the form described in 8.5 p16, bullet 7:
Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload resolution (13.3). The user-defined conversion so selected is called to convert the initializer expression into the object being initialized. If the conversion cannot be done or is ambiguous, the initialization is ill-formed.
The "enumeration of applicable conversion functions" is detailed in 13.3.1.5 p1:
Under the conditions specified in 8.5, as part of an initialization of an object of nonclass type, a conversion
function can be invoked to convert an initializer expression of class type to the type of the object being
initialized. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1
T” is the type of the object being initialized, and “cv S” is the type of the initializer expression, with S a
class type, the candidate functions are selected as follows:
The conversion functions of S and its base classes are considered. Those non-explicit conversion
functions that are not hidden within S and yield type T or a type that can be converted to type T
via a standard conversion sequence (13.3.3.1.1) are candidate functions. For direct-initialization, those
explicit conversion functions that are not hidden within S and yield type T or a type that can be
converted to type T with a qualification conversion (4.4) are also candidate functions. Conversion
functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type
for this process of selecting candidate functions. Conversion functions that return “reference to cv2
X” return lvalues or xvalues, depending on the type of reference, of type “cv2 X” and are therefore
considered to yield X for this process of selecting candidate functions.
Note that both A::operator A*() and A::operator B*() are candidate functions, since both A* and B* are convertible to void* per
4.10p2: "A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer
to cv void”." Given that both functions are candidates, overload resolution must choose between them.
Overload resolution is decribed in 13.3 [over.match]. p2 states:
Overload resolution selects the function to call in seven distinct contexts within the language:
...
invocation of a conversion function for initialization of an object of a nonclass type from an expression of class type (13.3.1.5)
...
Each of these contexts defines the set of candidate functions and the list of arguments in its own unique way. But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases:
First, a subset of the candidate functions (those that have the proper number of arguments and meet certain other conditions) is selected to form a set of viable functions (13.3.2).
Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.
Which of our two functions are viable? 13.3.2 [over.match.viable] p1:
From the set of candidate functions constructed for a given context (13.3.1), a set of viable functions is
chosen, from which the best function will be selected by comparing argument conversion sequences for the
best fit (13.3.3).
The requirements are presented in p2:
First, to be a viable function, a candidate function shall have enough parameters to agree in number with
the arguments in the list.
and p3:
Second, for F to be a viable function, there shall exist for each argument an implicit conversion sequence
(13.3.3.1) that converts that argument to the corresponding parameter of F.
Both requirements are trivially met by our conversion functions: they have a single (implicit) argument of the same type as the initializer expression a.
Determination of the best of the viable functions is described in 13.3.3 [over.match.best]. It defines some formalisms for describing conversion sequences, sequences of operations necessary to convert from the types of the actual function arguments to the types of the formal function parameters. In the case of our conversion functions, they both have exactly one parameter whose type is exactly that of the actual argument, so the "conversion sequence" corresponding to each overload is the identity sequence. They are discriminated by the language in p1:
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,
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.
What about that final bullet? Does one of our overloads have a better standard conversion sequence from its return type to void*?
13.3.3.2 Ranking Implicit Conversion Sequences [over.ics.rank] p4 states in the second bullet point:
If class B is derived directly or indirectly from class A, conversion of B* to A* is better than conversion of B* to void*, and conversion of A* to void* is better than conversion of B* to void*.
This is exactly the case of the OP, except with the names A and B reversed. Overload resolution on the two conversion operators of the OP is resolved in favor of A::operator B*() since the cited rule makes the conversion sequence B* → void* better than A* → void*

Ambiguous assignment operator

I have two classes, one of which, say, represents a string, and the other can be converted to a string:
class A {
public:
A() {}
A(const A&) {}
A(const char*) {}
A& operator=(const A&) { return *this; }
A& operator=(const char*) { return *this; }
char* c;
};
class B {
public:
operator const A&() const {
return a;
}
operator const char*() const {
return a.c;
}
A a;
};
Now, if I do
B x;
A y = x;
It triggers copy constructor, which compiles fine. But if I do
A y;
y = x;
It complains about ambiguous assignment, and can't choose between =(A&) and =(char*). Why the difference?
There is a difference between initialization and assignment.
In initialization, that is:
A y = x;
The actual call depends on the type of x. If it is the same type of y, then it will be like:
A y(x);
If not, as in your example, it will be like:
A y(static_cast<const A&>(x));
And that compiles fine, because there is no ambiguity any more.
In the assignment there is no such special case, so no automatic resolution of the ambiguity.
It is worth noting that:
A y(x);
is also ambiguous in your code.
There is §13.3.1.4/(1.2), only appertaining to (copy-)initialization of objects of class type, that specifies how candidate conversion functions for your first case are found:
Under the conditions specified in 8.5, as part of a
copy-initialization of an object of class type, a user-defined
conversion can be invoked to convert an initializer expression to the
type of the object being initialized. Overload resolution is used to
select the user-defined conversion to be invoked. […] Assuming that
“cv1 T” is the type of the object being initialized, with T a class
type, the candidate functions are selected as follows:
The converting constructors (12.3.1) of T are candidate
functions.
When the type of the initializer expression is a class type
“cv S”, the non-explicit conversion functions of S and its base
classes are considered. When initializing a temporary to be bound to
the first parameter of a constructor where the parameter is of type
“reference to possibly cv-qualified T” and the constructor is called
with a single argument in the context of direct-initialization of an
object of type “cv2 T”, explicit conversion functions are also
considered. Those that are not hidden within S and yield a type
whose cv-unqualified version is the same type as T or is a derived
class thereof are candidate functions. […] Conversion functions that return “reference to X” return lvalues or xvalues,
depending on the type of reference, of type X and are therefore considered to yield X for this process of selecting candidate functions.
I.e. operator const char* is, though being considered, not included in the candidate set, since const char* is clearly not similar to A in any respect. However, in your second snippet, operator= is called as an ordinary member function, which is why this restriction doesn't apply anymore; Once both conversion functions are in the candidate set, overload resolution will clearly result in an ambiguity.
Note that for direct-initialization, the above rule doesn't apply either.
B x;
A y(x);
Is ill-formed.
A more general form of this result is that there can never be two user-defined conversions in one conversion sequence during overload resolution. Consider §13.3.3.1/4:
However, if the target is
the first parameter of a constructor or […]
and the constructor […] is a candidate
by
13.3.1.3, when the argument is the temporary in the second step of a class copy-initialization, or
13.3.1.4, 13.3.1.5, or 13.3.1.6 (in all cases),
user-defined conversion sequences are not considered. [Note: These
rules prevent more than one user-defined conversion from being applied
during overload resolution, thereby avoiding infinite recursion. — end
note ]