Search code examples
c++c++11copy-elision

copy elision visible side effect


Consider this code:

#include <iostream>

using namespace std;

struct Foo {
public:
    int _a{};
    Foo(int a) : _a{a} 
    {
        std::cout << "ctor" << std::endl;
    }

    Foo(const Foo &)
    {
        std::cout << "copy" << std::endl;
    }
};

int main () {
    Foo a{10};
    Foo b = 10;
    std::cout << b._a << std::endl;
}

When I compile with

g++ -std=c++11 -fno-elide-constructors test.cpp

the output is

ctor ctor copy 0

which is what I expect, since the in Foo b = 10, 10 is implicitly constructed from int, then b is copy constructed from Foo. Furthermore, my copy constructor doesn't do anything, so the member _a remains 0 (as it is in-class-initialized).

However, when I use copy elision

g++ -std=c++11 test.cpp

the output is

ctor ctor 10

which is surprising to say the least. I understand that the copy constructor is elided here, but this is a serious side-effect (the fact that the member is once initialized to 0 and once to 10), as it affects the rest of the code path.

Is this behaviour normal?


Solution

  • The whole point of singling copy elision out as a on optimization in specific cases is to allow eliding side effects of copy construction. That is, yes, it expected that copy elision happens despite the copy constructor and/or the destructor having side effects.

    If you don't want copy elision to happen in certain cases you'll need to arrange for it to be inhibited. In your specific case it is admittedly a bit annoying to inhibit copy elision but something like this should do the trick:

    template <typename T>
    T const& inhibit(T const& ref) {
        return ref;
    }
    // ...
    Foo b = inhibit<Foo>(10);