Reputation: 2734
This is a follow-up of this question: conditional operator expression with base and const derived class doesn't compile, why?.
The core is cond ? [cv] T1() : [cv] T2()
where, for instance T2
publicly inherit from T1
.
Trying to follow the rules for determining the type of the expression, it seemed to require an implicit conversion sequence from the derived object to the base one. Yet implicit conversion rules do not cover derived object to base object conversion (only pointer/reference to derived implicit conversion to pointer/reference to base).
In linked question example, though, the types were really simple:
struct X {};
struct Y : X {};
And thus they have implicit default move and copy constructors which may allow the implicit conversion sequence to be formed.
In order to test this hypothesis, I tried to explicit these operators, to break the conversion sequence:
#include <iostream>
struct X {
X() = default;
explicit X(X const &) { std::cout << "copy to base\n"; }
explicit X(X &&) { std::cout << "move to base\n"; }
virtual ~X() = default;
};
struct Y : X {
Y() = default;
Y(Y const &y) : X(y) { std::cout << "copy to derived\n"; }
Y(Y &&) { std::cout << "move to derived\n"; }
virtual ~Y() = default;
};
int main() { true ? Y() : X(); }
I tested different combinations with the explicit keywords and I got these output:
explicit | gcc | clang | msvc |
---|---|---|---|
none | move to base | move to base | move to base |
on move only | move to base (expected copy) | copy to base | move to base (expected copy) |
on copy only | move to base | move to base | move to base |
on both | move to base (expected error) | error | error |
My expectation was: if an implicit move constructor exists, use it in a conversion sequence, otherwise, if a copy constructor exists, use it in a conversion sequence, otherwise there is no implicit conversion sequence.
Why are the output different from a compiler to another and why don't they match my expectation?
NB The conditional operator usage seems really meaningful for the question because replacing it by a function call gives output more in line with my expectation, except with msvc:
void foo(X) {};
int main() { foo(Y()); }
LIVE In this case, only msvc behaves strangely by always using the move operator. I think it might be a msvc bug.
Upvotes: 3
Views: 62