[C++] Copy constructor optimization

Started by
21 comments, last by FFFF0000 15 years, 8 months ago
Quote:Original post by samoth
The compiler can't just assume that it doesn't matter whether or not it initializes something just because it feels like it.


It can, because the standard says it can.

The fact that it compiles on some platforms and not others is in the wording of the standard. It says an implementation "is allowed" to do it, not that it is required to do it.

Visual Studio is doing nothing that it is not allowed to do. It is not changing the meaning of the program in any way that is not allowed by the standard. It is not subverting the access protection for the base class copy constructor, it is not calling the copy constructor. The paragraph I quoted from the standard in my previous post says it is allowed to do that in certain circumstances, this is one of them.

Advertisement
Quote:Original post by FFFF0000
It can, because the standard says it can.
That's funny, because my copy of the standard says:
Even when the creation of the temporary object is avoided (12.8), 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 11), shall be satisfied.]


Maybe I'm reading it wrong, but to me it isn't obvious that what Visual Studio is doing there is right.
For the code to be valid per the standard, it needs to have an accessible copy constructor, simply because a compiler that is allowed to perform an optimization is not mandated to do so.

The compiler is also allowed, similarly, to detect instances of guaranteed undefined behaviour at compile-time and cause something useful to happen at runtime - for example, dereferencing a NULL pointer is allowed to throw an exception, for the reason that it's allowed to do *anything*. That doesn't make the code OK, however.
Quote:Original post by samoth
Quote:Original post by FFFF0000
It can, because the standard says it can.
That's funny, because my copy of the standard says:
Even when the creation of the temporary object is avoided (12.8), 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 11), shall be satisfied.]


Maybe I'm reading it wrong, but to me it isn't obvious that what Visual Studio is doing there is right.


Well I would argue that the accessibility restrictions are met for the copy constructor for the type of the class being optimized out. Its copy constructor is public, it is accessible.

The only way for the compiler to know if the optimization is meeting the accessibility restrictions of the base class is if it has the code for the copy constructor of the derived class visible. This will not always be the case. I doubt the standard would intend to place such a restriction on the optimization.

As with most things though, it comes down to interpretation.
Quote:Original post by Zahlman
For the code to be valid per the standard, it needs to have an accessible copy constructor, simply because a compiler that is allowed to perform an optimization is not mandated to do so.


What does "accessible" mean in this context?

What about this example:

Uncopiable foo(Uncopiable uc){    //...    return uc;}//...    Uncopiable x;    x = foo(x);


Are you saying that if the compiler realizes that it doesn't need to make any copies here, this faulty code is allowed to be compiled?

It seems unbelievable that the syntactic correctness of some piece of code would depend on how good your compiler is at optimizations. (I think this is the point of 12.2.1 quoted above, which also seems to make VC++ wrong: even though it can avoid the temporary, the code explicitly tries to use the private copy constructor. The compiler should not be allowed to silently "correct" your code.)
Quote:Original post by FFFF0000
Well I would argue that the accessibility restrictions are met for the copy constructor for the type of the class being optimized out. Its copy constructor is public, it is accessible.
Well, but you see... omitting a copy constructor is an optimization.
Optimizations are done only after the program has been parsed, syntax-checked, and otherwise checked for validity.
So, whether or not the compiler skips the copy constructor or any empty base constructors later, it still has to presume it's calling them, initially. And it still has to fail if it can't access them. Otherwise it can't be sure the program is valid at all. Violating access rules is a program error, and a program with errors must not compile.
Basically, that's what the standard says, in other words (at least, it's how I read it).
Well the way I see it, the intention of the author of the class is that the copy constructor of the base class should not be called. The compiler is not breaking that intention.

Given that the copy constructor is unlikely to be in the same translation unit as the offending code, the compiler has to make some assumptions. If the class has a user defined copy constructor, then the compiler can safely assume that the copy constructor is valid. If the user defined copy constructor was not valid then the translation unit that contained the user defined copy constructor would not compile. And so the compiler can safely apply the optimization in this case.

The second case is the copy constructor being implicitly declared or defined. In this case the compiler has to generate a copy constructor for the class if one is used. In the example given, it would be the copy constructor that would fail to compile, as it would try to access the base class copy constructor. The offending code simply serves to generate that copy constructor.

So it looks like your argument is that the compiler should attempt to optimize the call to the copy constructor out after it has generated the copy constructor. My argument is that it should attempt to optimize the call to the copy constructor out before it tries to generate one.

Now given Visual Studios implementation, they have provided a way to prevent Foo f(Foo(42)); from compiling. You simply need to make the derived class copy constructor private. To me that would increase readability. The class may also work with unmodifiable library code that might semantically look like it requires a copy, but in fact can be implemented without it. This will increase the reuse of your class, though the code would not be portable.

Without the VS implementation, your code would not be able to work with the same library code. On the positive side though, it would force uses such as Foo f(Foo(42)); to be cleaned up.

Summing both up, I would prefer the VS implementation. It allows you to stop Foo f(Foo(42)); from compiling by making the Foo copy constructor private, and it could possibly work with library code that the non-VS implementation could not.
Quote:
Now given Visual Studios implementation, they have provided a way to prevent Foo f(Foo(42)); from compiling. You simply need to make the derived class copy constructor private. To me that would increase readability.


I thought the whole point of inheriting it from NoCopy was to make attempts of copy like that illegal. That's also what boost::noncopyable is for, so that you wouldn't have to declare a private copy constructor/operator= in every class you don't want to be copyable.

Since it seems that many other compilers disagree with VC++, you should simply avoid lines like that (looks stupid anyway).
VS 2008 is broken:
#include <boost/utility.hpp>class Foo : boost::noncopyable {public:	Foo(int) {}};int main(){	Foo f1(Foo(5));      // this compiles	Foo f2(Foo(Foo(5))); // this does not}

This topic is closed to new replies.

Advertisement