C ++: calling the constructor in a temporary object

Suppose I have the following:

int main() { SomeClass(); return 0; } 

Without optimization, the SomeClass () constructor is called, and then its destructor will be called, and the object will be larger.

However, according to the IRC channel, a call to the constructor / destructor can be optimized if the compiler does not consider a side effect for SomeClass constructors / destructors.

I assume that the obvious way to do this is not to use any constructor / destructor function (for example, to use a function or a static method or so), but is there a way to provide a call to constructors / destructors?

+4
source share
3 answers

However, according to the IRC channel, a call to the constructor / destructor can be optimized if the compiler does not see a side effect for SomeClass constructors / destructors.

The bold part is incorrect . It should be: knows there is a lack of observable behavior

eg. from Β§ 1.9 of the last standard (there are more relevant quotes):

The corresponding implementation, executing a well-formed program, should provide the same observable behavior as one of the possible executions of the corresponding instance of an abstract machine with the same program and the same input. However, if any such execution contains an undefined operation, this International Standard Seats does not require the execution of this program with this input (not even for operations preceding the first undefined operation).

In fact, this whole mechanism underlies the synergy of the most ubiquitous C ++ idiom: Resource Initialization

Backgrounder

To optimize the compiler, trivial case constructors are extremely useful. This is what allows iterators to compile to the exact same performance code, using raw pointers / indexers.

It also allows the function object to compile the same code as the embedding of the function body.

This is what makes C ++ 11 lambdas completely optimal for simple use cases:

 factorial = std::accumulate(begin, end, [] (int a,int b) { return a*b; }); 

A lambda compiles to a functor object similar to

 struct lambda_1 { int operator()(int a, int b) const { return a*b; } }; 

The compiler sees that the constructor / destructor can be discarded and the body of the function will turn out to be nested. The end result is optimal 1


More (un) observed behavior

The standard contains a very interesting example, on the contrary, to spark your imagination.

Β§ 20.7.2.2.3

[ Note: Usage count updates caused by the creation and destruction of a temporary object are not observable side effects, so an implementation can satisfy effects (and implied warranties) through a variety of ways, without creating a temporary one. In particular, in the example:

 shared_ptr<int> p(new int); shared_ptr<void> q(p); p = p; q = p; 

both appointments can be non-op. β€”end note ]

IOW: Don't underestimate the power of compiler optimization. This in no way means that language guarantees should be thrown out of the window!

1 Although there may be faster algorithms for obtaining factorial, depending on the problem area :)

+7
source

I am sure that "SomeClass :: SomeClass ()" is not implemented as "inline", the compiler does not know that the constructor / destructor has no side effects, and it will always call the constructor / destructor,

0
source

If the compiler optimizes the visible effect of calling the constructor / destructor, it does not work. If it does not have a visible effect, you still will not notice it.

However, suppose that somehow your constructor or destructor really has a visible effect (therefore, building and subsequently destroying this object is not effectively non-op) in such a way that the compiler could legitimately think that it will not (not that I can think of such a situation, but then it may just be a lack of imagination on my side). Then any of the following strategies should work:

  • Make sure that the compiler does not see the constructor and / or destructor definition. If the compiler does not know what the constructor / destructor does, he cannot assume that it has no effect. Please note, however, that this also disables embedding. If your compiler does not perform cross-module optimization, just add a constructor / destructor to another file.

  • Make sure your constructor / destructor does have observable behavior, for example. through the use of volatile variables (every read or write of a mutable variable is considered as observable behavior in C ++).

However, let me emphasize once again that it is very unlikely that you need to do anything if your compiler is badly buggy (in this case, I strongly recommend that you change the compiler :-)).

0
source

Source: https://habr.com/ru/post/1383314/


All Articles