A: 

I can't point you at a specific section of the standard, but it certainly looks OK to me, and compiles with Comeau C++ as well as the compilers you mention. As for a name for such a test, I guess "compiler compatibility" is as good as anything.

anon
Thanks for the confirmation. However, I'd like a more specific name than just "compiler compatibility". If you saw something like "object slicing support" without above context, would that be understandable enough for you?
Michael Wild
+4  A: 

I dare to disagree with Comeau in this case. In fact, the following code fails to compile as expected, because binding an rvalue to a const reference requires accessible copy constructor.

class A {
};

class B : public A {
  B(const B& other);
  B& operator=(const B& other);
public:
  explicit B(){}
};

int main()
{
    A const & a = B();
}

Per 8.5.3/2, "[...] Argument passing (5.2.2) and function value return (6.6.3) are initializations," and as such the code should be ill-formed.

Edit: I still firmly believe that the code is ill-formed according to C++03. However, I've just read the relevant section of the working draft for C++0x and it seems that it no longer requires the copy constructor to be available. Perhaps that's the reason your code started compiling when you moved from gcc-4.2 to gcc-4.3.

Edit: To clarify, the reason why B::B(const B &) must be accessible is due to the binding of B() to the first parameter of A::A(const A &) (which is, of course, called when m_a is being initialized).

Edit: Regarding the difference between C++03 and C++0x, litb was kind enough to find the relevant defect report.

avakar
But wouldn't that mean that also something like the following is invalid? class A { A(const A A }; void f(const A f( a ); }
Michael Wild
Sorry, just noticed that comments don't format...
Michael Wild
But why then does "int main(){B b; A const}" work? Is it something to do with temporary objects?
Michael Wild
In the original code, there is an accessible copy constructor - the one belonging to class A.
anon
Michael, in `int main(){B b; A const}`, the expression `b` is an lvalue, whereas `B()` would be an rvalue.
avakar
Neil, but you're initializing a const reference with an rvalue expression of type `B`, where `B` is a class type, and as such copy constructor of `B` must be available.
avakar
I disagree - in the original code, you are initialising a object of type A, which means that A's (not B's) copy constructor will be used. and this is available.
anon
Neil, no, standard clearly says: "A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows [...] A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary". Note, that the "sic" is not mine, it's part of the text of the standard.
avakar
Yes, it says "a constructor is called" - it doesn't say which one, so doesn't back your position up.
anon
How can a temporary of type "cv1 T2" be created using T1's copy constructor?
avakar
Well, through implicit conversion, because T2 inherits from T1.
Michael Wild
Michal, in C++, constructors are not inherited.
avakar
No need to, I call it explicitly in "m_a(B())". All the compiler needs to do is to convert a (temporary) object of type B to type A, which definitely is standard.
Michael Wild
avakar
Michael Wild
The current standard is c++03 and it definitely requires accessible copy constructor on rvalue binds. As I said in my answer, the draft of the upcoming standard lifted that requirement. g++ and other popular compilers are in general already trying to implement changes that seem unlikely to be modified in the final standard.
avakar
Just for completeness; section 12.2/1 says it clearly: Even when the creation of the temporary object is avoided (class.copy), all the semantic restrictions must be respected as if the temporary object was created. [Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (clause class.access), shall be satisfied. ]
Michael Wild
fnieto
@fnieto, no i just tested too, and it accepts.
Johannes Schaub - litb
I could have been more clear, I was referring to OP's code, not mine. Anyway, I've removed the line.
avakar
You may wanna mention http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#391 , tho.
Johannes Schaub - litb
Done, thank you for the link. :)
avakar
What is the difference with `A const ` ? Last variant compiles OK.
Kirill V. Lyadvinsky
I don't think it should compile OK, just as `A b(B());` shouldn't.
avakar
Don't be confused of `A b(B());`, if it works. It doesn't do what you want.
Johannes Schaub - litb
litb, I'm not sure what you mean. I mentioned `A b(B());` because `static_cast<A const` works if and only if `A b(B());` works. And since it doesn't, the `static_cast` doesn't work either.
avakar
@litb, about fnieto comment: if you disable c++0x extensions and force c++03 compliance then the code will not compile with comeau with the error reported by fnieto.
David Rodríguez - dribeas
What exactly is the problem with 'A a( B() )' (assuming c++0x)? How would it differ from what you expect? (I would expect slicing, if that is the point)
David Rodríguez - dribeas
dribeas, in C++0x there is no problem, `A a(B())` is well-formed and causes the object `a` to be created using `A`'s copy constructor with `B()` bound to its only (reference) parameter. The problem is in C++03, where the binding additionally requires `B` to have an accessible copy constructor.
avakar
avakar
@litb We were testing diferent code. You are right, the code form the answer compile, but not the code form the @Michael Wild answer.
fnieto
@dribeas, no the code in the question compiles with the comeau online compiler, without extensions and with strict mode.
Johannes Schaub - litb
@avakar, there is no object created by `A a(B())`. I was just noting that, and pointing that you need to do `A a((B()));`
Johannes Schaub - litb
@avak, The text in the Standard says that it is valid iff `A const` is valid. You of course need to include the reference. otherwise, you say "`static_cast<int` is valid".
Johannes Schaub - litb
litb, as always, you're right on both accounts :) Regarding `A a((B()));`: the most vexing parse problem is something I do not automatically spot and I'm bound to do the same mistake over and over again in the future. Re `A const` I dropped the reference by mistake and carried it all over my comments. Sorry, my bad.
avakar
Don't worry. I was kidding somewhat when i made these remarks, keeping the reason why it's not rejected "hidden" :)
Johannes Schaub - litb
+1  A: 

Seems like older g++ compilers can't pass temporary objects by reference if there is no copy-constructor available:

class A { 
  A(const A& other);
  A& operator=(const A& other);
public:
  explicit A(){}
};
void f( const A& a ) {}
int main() {
  A a;
  f( a );    // fine
  f( A() );  // fails
}
Michael Wild
But that is not the situation in your original code. A copy constructor for A (the default one) is available.
anon
+1  A: 

I think it is standard-conforming code in C++0x, but not in C++03.

I'd name the test something like "copy construction from rvalue".

This was reported as an error, but gcc people argue here that is the correct behavior and give references to the standard.

[dcl.init.ref]/5, bullet 2, sub-bullet 1

If the initializer expression in 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 the 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.

C++0x standard removes the ambiguity and the reference is always bounded to the object represented by the rvalue, not needing the constructor to be accessible.

fnieto
A: 

It is valid. Calling B() constructs a B object; B's default compiler generated public constructor is called.

When a C object is constructed an A object will be constructed. As it is a value then static typing will be considered and slicing will occur on any object that is derived from A and that is used to copy construct an A object. Class A has a public copy constructor provided by the compiler. The compiler sees that a B object is also of type A. The compiler is only concerned with copy constructing an A object and it knows it can do this so it copies the A object within the B object using the A copy constructor to the C::m_a object. ie slicing due to static typing.

It is instructive to look at the memory of these objects. Put a char * data member in A initialised to "I'm an A" and a char * data member in B initialised to "I'm a B" and step through your debugger to monitor your objects. You should see those strings easy enough.

Sam
I do agree, it is sensible to expect it to work as you say. Question is, does the standard agree? I know that there are some rather tricky issues related to temporaries...
Michael Wild
`B` doesn't have a compiler-generated default constructor and wouldn't have it even if the default constructor weren't declared explicitly.
avakar
@avakar - oops! for overlooking the declared B::B(). And your quite right again; B has a user declared copy constructor, it is a constructor, and so no compiler generated default constructor. Thanks.@Michael Wild - you know I've often tried to verify things like this in the C++ Standard but failed to pin them down. This is one of those! Slicing is not in the index.
Sam