The following code compiles fine with g++ (GCC) 4.7.1 20120721, but
fails with a recently build clang version 3.2 (trunk).
struct Y {};
struct X {
operator const Y() const { return Y(); }
};
void f(Y&& y) {}
int main()
{
f(X());
return 0;
}
Changing the conversion operator to operator Y() const is sufficient
to make the code compile on both compilers.
Which compiler is actually standard compliant in this case? What does
the standard actually say about this?
The verbatim error as requested:
bla.cpp:14:5: error: no viable conversion from 'X' to 'Y'
f(X());
^~~
bla.cpp:1:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'X' to
'const Y &' for 1st argument
struct Y {
^
bla.cpp:1:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'X' to
'Y &&' for 1st argument
struct Y {
^
bla.cpp:6:3: note: candidate function
operator const Y() const { return Y(); }
^
bla.cpp:10:12: note: passing argument to parameter 'y' here
void f(Y&& y) {}
^
EDIT: Unfortunately even adding the overload
void f(const Y&) {}
still makes clang choose the rvalue reference overload and so this
breaks existing code that used to compile fine, e.g. with standard
containers.
I believe clang is right to reject this. Passing the argument to f(Y&&) requires two conversion steps, the first being your operator const Y() and the second being Y's copy constructor. Both count as user-defined conversions, I think, and both are implicit, which violates the principle that an implicit conversion sequence only includes one user-defined conversion.
This Purpose of returning by const value? contains some interesting insights into the semantics of returning a const T.
Hm, if I try adding an overload void f(const Y&y) as the edited question now does, clang behaves pretty strangly. It still complains about being unable to convert X to Y, and doesn't even list the overload f(const Y& y) in its diagnostic. But once I change the overload to take Y by value, i.e. write void f(const Y y), it complains about the call to f being ambiguous.
This is with XCode 4.5's clang which reports Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn). If you can reproduce this with a vanilla clang, you should probably report this on the clang mailing list - sure seems to there's a bug lurking there somewhere...
The example is ill-formed.
Some N3242 quotes.
8.5.3 paragraph 4:
Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if T1 is reference-related to T2 and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2.
paragraph 5 (bullet labels are mine):
A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:
If the reference is an lvalue reference and ...
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e. cv1 shall be const), or the reference shall be an rvalue reference.
a. If the initializer expression
i. is an xvalue, class prvalue, array prvalue or function lvalue and "cv1 T1" is reference-compatible with "cv2 T2", or
ii. has a class type (i.e. T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an xvalue, class prvalue, or function lvalue of type "cv3 T3", where "cv1 T1" is reference-compatible with "cv3 T3",
then the reference is bound to ....
b. Otherwise, a temporary of type "cv1 T1" is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the temporary. If T1 is reference-related to T2, cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2. ...
For the function parameter initialization, T1 is Y, T2 is X, and cv1 and cv2 are both empty. 1 is out: the reference is an rvalue reference, not lvalue reference. 2.a.i. is out: X is not reference-compatible with Y. 2.b. is out because copy-initializing an Y from a prvalue of type X involves two user-defined conversions: the conversion function and then the copy constructor of Y. (The exact prohibition is in 13.3.3.1p4.)
For case 2.a.ii., the obvious choice for "cv3 T3" is const Y, but that's no good because Y is not reference-compatible with const Y. You might argue for trying T3 is Y and cv3 is empty, but then you're back to needing the copy constructor of Y as the second implicit user-defined conversion.
Related
This is a follow-up of this question.
I played with this code and used clang trunk and gcc trunk:
struct A
{
};
struct T
{
A a;
operator A() { return a; }
template <typename T> operator const T&() = delete;
};
struct C
{
A a;
};
int main()
{
C c;
T t;
c.a = t;
}
Demo
Clang has nothing to complain, but gcc has:
<source>: In function 'int main()':
<source>:23:11: error: use of deleted function 'T::operator const T&() [with T = A]'
23 | c.a = t;
| ^
<source>:10:27: note: declared here
10 | template <typename T> operator const T&() = delete;
|
^~~~~~~~
So, which compiler is right and which is wrong?
I would like to see gcc is wrong, but how can I overcome the error?
Both Clang and GCC are wrong, but at least GCC stops compilation so it's marginally better.
A is a class type, and as such assigning to it passes through an operator= overload. You did not provide one, so the compiler provides two to the effect of
A& operator=(A const&) = default;
A& operator=(A&&) = default;
That reference argument is what needs to get initialized from t in the expression c.a = t. And for either operator=, the reference can be bound unambiguously.
[dcl.init.ref]
5 A reference to type “cv1 T1” is initialized by an expression of
type “cv2 T2” as follows:
If the reference is an lvalue reference and the initializer expression
[...]
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an lvalue of type
“cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (this
conversion is selected by enumerating the applicable conversion
functions ([over.match.ref]) and choosing the best one through
overload resolution),
then the reference is bound ... to the lvalue result of the
conversion in the second case (or, in either case, to the appropriate
base class subobject of the object).
Otherwise, if the initializer expression
[...]
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an rvalue or function
lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with
“cv3 T3” (see [over.match.ref]),
then ... the result of the conversion in the second case is called
the converted initializer. If the converted initializer is a prvalue,
its type T4 is adjusted to type “cv1 T4” ([conv.qual]) and the
temporary materialization conversion ([conv.rval]) is applied. In any
case, the reference is bound to the resulting glvalue (or to an
appropriate base class subobject).
Where on the subject of building that candidate set for these two cases, the standard says
[over.match.ref]
1 Under the conditions specified in [dcl.init.ref], a reference can
be bound directly to the result of applying a conversion function to
an initializer expression. Overload resolution is used to select the
conversion function to be invoked. Assuming that “reference to cv1 T”
is the type of the reference 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 “lvalue reference to cv2 T2” (when initializing an
lvalue reference or an rvalue reference to function) or “cv2 T2” or
“rvalue reference to cv2 T2” (when initializing an rvalue reference or
an lvalue reference to function), where “cv1 T” is
reference-compatible with “cv2 T2”, are candidate functions. For
direct-initialization, those explicit conversion functions that are
not hidden within S and yield type “lvalue reference to cv2 T2” (when
initializing an lvalue reference or an rvalue reference to function)
or “rvalue reference to cv2 T2” (when initializing an rvalue reference
or an lvalue reference to function), where T2 is the same type as T or
can be converted to type T with a qualification conversion, are also
candidate functions.
That bullet needs a bit of work to parse correctly, but it basically describes one of two disjoint cases that may apply here:
Initialization of an lvalue reference to T
The candidate functions are those that yield "lvalue reference to cv2 T2".
Initialization of an rvalue reference to T
The candidate functions are those that yield "cv2 T2" or "rvalue reference to cv2 T2".
For operator=(A const&) we are in case #1, and have a synthesized operator A const&() as the only candidate. For operator=(A&&) its case #2, and the non-template operator A() is the only candidate. Either way, we have an unambiguous implicit conversion sequence with a user-defined conversion that binds the reference parameter of either operator=.
But now neither operator= is a better viable function than the other according to the rules in [over.match.best]. And neither conversion is better according to the partial ordering in [over.ics.rank].
This means that the the program should be declared ill-formed on account of an ambiguous call to operator=. However, GCC and Clang both err (not MSVC though)1. Clang favors the A&& overload, while GCC goes for A const& and issues a diagnostic for the use of a deleted conversion function. But this isn't due to any standard mandated behavior. Ideally, they should both report the call to operator= is ambiguous.
1 - A comparison of different compiler behaviors, with a reduced example.
#include <iostream>
struct B;
struct A{
operator B&&() const;
};
struct B{
B(A const&){
}
B() {}
};
int main(){
A a;
B&& rf = a; //#1
}
B g;
A::operator B&&() const {
std::cout<<"execute\n";
return std::move(g);
}
Consider the above code, The outcome is here. The reference binding at #1 subject to these rules:
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
If the initializer expression
is an rvalue (but not a bit-field) or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an rvalue or function lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (see [over.match.ref]),
then the value of the initializer expression in the first case and the result of the conversion in the second case is called the converted initializer. If the converted initializer is a prvalue, its type T4 is adjusted to type “cv1 T4” ([conv.qual]) and the temporary materialization conversion is applied. In any case, the reference is bound to the resulting glvalue (or to an appropriate base class subobject).
Otherwise:
If T1 or T2 is a class type and T1 is not reference-related to T2, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion ([dcl.init], [over.match.copy], [over.match.conv]); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed. The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference. For this direct-initialization, user-defined conversions are not considered.
According to the structure of the above rules, The second bullet of the If branch is sufficient for B&& rf = a;, hence operator B&&() const of class A is the unique candidate conversion function, In other words, as long as the if case be satisfied, then the branch of otherwise will never under go.
The outcome of GCC evidence what these rules says, however Clang complain the conversion function for performing reference binding are ambiguous(Clang seems to consider both conversion functions in respectively branch as candidate functions). Is it a bug in clang?
Even though, such case
#include <iostream>
struct B;
struct A{
operator B&&() const;
};
struct B{
B(A&){
}
B() {}
};
int main(){
A a;
B&& rf = a; //#1
}
B g;
A::operator B&&() const {
std::cout<<"execute\n";
return std::move(g);
}
GCC still agree operator B&&() const is the unique conversion function for performing reference binding.
Yes you sited the right paragraph [dcl.init.ref] §5.2.1.2:
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
If the initializer expression
is an rvalue [...]
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an rvalue or function lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (see [over.match.ref]),
The intent of reference initialization is to first try to bind directly (the definition is in the last normative sentence of the section [dcl.init.ref], in short: direct binding happens when the initializer and the reference are reference related or if there is a conversion function whose result type is reference related to the initialized reference). So this is a Clang bug and this is certainly not an open issue in the standard.
(The sited open issues (CWG2028) that may have cause doubts is related to the fact that some "direct binding" may involve a temporary materialization, so that those case should not be direct reference binding but indirect binding to the result of an user defined conversion.)
For example, in the snippet below the user-defined conversion function C::operator A() is implicitly invoked to convert an lvalue of type C into a prvalue of type A, which copy-initializes the variable a in a direct-initialization.
struct A {};
struct C { operator A() { return A(); }; };
int main()
{
C c;
A a(c);
}
I just want to know where this is described in the C++14 Standard. I have a feeling that the answer is in [over.match.copy]/1 bullet point (1.2), but I'm having a problem with the section title Copy-initialization by user-defined conversion.
There are two constructors on A that can be invoked with a single argument:
A(A const&); // copy constructor
A(A&& ); // move constructor
In either case, [dcl.init.ref] explains how we can initialize the reference:
A reference of type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:
— If the reference is an lvalue reference and the initializer expression
— is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2”, or
— has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (this conversion is selected by enumerating the applicable conversion functions ([over.match.ref]) and choosing the best one through overload resolution),
then the reference is bound to the initializer expression lvalue in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).
We have references of type A or A const being initialized by an expression of type C, which is a class type not reference-related to A. To check if it can be converted to a reference-compatible type cv3 T3, we check [over.match.ref]:
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 “lvalue reference to cv2 T2” (when initializing an lvalue reference or an rvalue reference to function) or “cv2 T2” or “rvalue reference to cv2 T2” (when initializing an rvalue reference or an lvalue reference to function), where “cv1 T” is reference-compatible with “cv2 T2”, are candidate functions. For direct-initialization, [...]
Hence, for the copy constructor, we consider those conversion functions that yield A& and for the move constructor, we consider those conversion functions that yield A or A&&. We don't have the former, but we do have the latter: operator A().
This makes the move constructor of A a viable constructor, but not the copy constructor of A. Since we only have one viable candidate, it is trivially the best viable candidate.
I've encountered a problem with implicit conversion in C++. The following is a minimal example:
struct A {
virtual void f()=0; // abstract
};
struct Ad : A {
virtual void f() {} // not abstract
};
struct B {
operator Ad () const { return Ad(); }
};
void test(A const &lhs) {}
int main()
{
B b;
test(b);
}
What I would like the compiler to do is: convert b to a variable of type Ad (using the conversion defined in B) and pass the result to test. However, the above code does not compile in GCC (with C++11 enabled), the result being Cannot allocate an object of abstract type 'A'.
Some things to note:
Clang compiles this.
If you make A non-abstract by changing f()=0; to f() {}, the code works just fine.
The compiler does find the conversion operator (as indicated by 2), but it doesn't do what I'd like it to do.
(All quotes from N4140, the C++14 FD)
TL;DR: The code is well-formed, this is (or was) a GCC bug.
The rules for reference initialization are covered in [dcl.init.ref]/5. I'll first show you the bullet that doesn't cover it - if you want to skip that go straight to the third quote.
Otherwise, the reference shall be an lvalue reference to a
non-volatile const type (i.e., cv1 shall be const), or the
reference shall be an rvalue reference.
If the initializer expression
is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”,
or
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an xvalue,
class prvalue, or function lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (see 13.3.1.6),
then the
reference is bound to the value of the initializer expression in the
first case and to the result of the conversion in the second case (or,
in either case, to an appropriate base class subobject).
And reference-compability is defined in [dcl.init.ref]/41.
Now consider the linked 13.3.1.6:
Under the conditions specified in 8.5.3, a reference can be bound
directly to a glvalue or class prvalue that is the result of applying
a conversion function to an initializer expression. Overload
resolution is used to select the conversion function to be invoked.
Assuming that “cv1 T” is the underlying type of the reference
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 “lvalue reference to cv2 T2”
(when initializing an lvalue reference or an rvalue reference to
function) or “cv2 T2” [..],
where “cv1 T” is reference-compatible (8.5.3) with “cv2 T2”,
are candidate functions. For direct-initialization, [..].
As you can see, your conversion function isn't a candidate after this paragraph. Thus the next bullet in [dcl.init]/5 is applicable:
Otherwise:
If T1 is a class type, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1
T1” by user-defined conversion (8.5, 13.3.1.4); the program is
ill-formed if the corresponding non-reference copy-initialization
would be ill-formed. The result of the call to the conversion
function, as described for the non-reference copy-initialization, is
then used to direct-initialize the reference. The program is
ill-formed if the direct-initialization does not result in a direct
binding or if it involves a user-defined conversion.
Note that the phrase "the program is
ill-formed if the corresponding non-reference copy-initialization
would be ill-formed" may imply that as
B b;
A a = b;
is ill-formed, the program is ill-formed. I believe this to be a defect or vagueness in the wording though, and not the reason that GCC does not accept the code. Assuredly the wording solely aims at the initialization itself, not the fact that a most-derived object of type T1 (aka A) can be created in the first place.
Finally 13.3.1.4 accepts our conversion function:
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. [..]. 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.
Now the last question is whether in
A const& ref(Ad());
ref is bound directly. And it is.
Thus the original parameter reference binds directly, and no most-derived object of type A must be created.
What GCC presumably thinks is that a temporary of type A must be initialized and the reference be bound to that temporary. Or it pedantically follows the above defected wording, which is very unlikely.
1)
Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to
“cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2.
“cv1 T1” is reference-compatible with “cv2 T2” if T1 is
reference-related to T2 and cv1 is the same cv-qualification as, or
greater cv-qualification than, cv2.
I'm puzzled with this behavior of C++:
struct A {
virtual void print() const { printf("a\n"); }
};
struct B : public A {
virtual void print() const { printf("b\n"); }
};
struct C {
operator B() { return B(); }
};
void print(const A& a) {
a.print();
}
int main() {
C c;
print(c);
}
So, the quiz is, what is the output of the program - a or b? Well, the answer is a. But why?
The problem here is a bug / misfeature / hole in the C++03 standard, with different compilers trying to patch over the problem in different ways. (This problem no longer exists in C++11 standard.)
Sections 8.5.3/5 of both standards specify how a reference is initialized. Here's the C++03 version (the list numbering is mine):
A reference to type cv1 T1 is initialized by an expression of type cv2 T2 as follows:
If the initializer expression
is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or
has a class type (i.e., T2 is a class type) and can be implicitly converted to an lvalue of type cv3 T3, where cv1 T1 is reference-compatible with cv3 T3
then the reference is bound directly to the initializer expression lvalue in the first case, and the reference is bound to the lvalue result of the conversion in the second case.
Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const).
If the initializer expression is an rvalue, with T2 a class type, and cv1 T1 is reference-compatible with cv2 T2, the reference is bound in one of the following ways (the choice is implementation-defined):
The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object.
A temporary of type cv1 T2 [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub-object within the temporary.
The constructor that would be used to make the copy shall be callable whether or not the copy is actually done.
Otherwise, a temporary of type cv1 T1 is created and initialized from the initializer expression using the rules for a non-reference copy initialization (8.5). The reference is then bound to the temporary.
There are three types involved in the question at hand:
The type of the reference to be created. The standards (both versions) denote this type as T1. In this case, it is struct A.
The type of the initializer expression. The standards denote this type as T2. In this case, the initializer expression is the variable c, so T2 is struct C. Note that because struct A is not reference-compatible with struct C, it's not possible to directly bind the reference to c. An intermediate is needed.
The type of the intermediate. The standards denote this type as T3. In this case, this is struct B. Note that applying the conversion operator C::operator B() to c will convert the lvalue c to an rvalue.
The initializations by what I labeled as 1.1 and 3 are out because the struct A is not reference-compatible with struct C. The conversion operator C::operator B() needs to be used. 1.2 is out Because this conversion operator returns an rvalue, this rules 1.2 out. All that is left is option 4, create a temporary of type cv1 T1. Strict compliance with the 2003 version of the standard forces the creation of two temporaries for this problem, even though only one will suffice.
The 2011 version of the standard fixes the problem by replacing option 3 with
If the initializer expression
is an xvalue, class prvalue, array prvalue or function lvalue and cv1 T1 is reference-
compatible with cv2 T2, or
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an xvalue, class prvalue, or function lvalue of type cv3 T3, where cv1 T1 is reference-compatible with cv3 T3,
then the reference is bound to the value of the initializer expression in the first case and to the result of the conversion in the second case (or, in either case, to an appropriate base class subobject). In the second case, if the reference is an rvalue reference and the second standard con- version sequence of the user-defined conversion sequence includes an lvalue-to-rvalue conversion, the program is ill-formed.
It appears that the gcc family of compilers chose strict compliance over intent (avoid creating unnecessary temporaries), while the other compilers that print "b" chose intent / corrections to the standard. Choosing strict compliance isn't necessarily commendable; there are other bugs/misfeatures in the 2003 version of the standard (e.g., std::set) where the gcc family chose sanity over strict compliance.