Fabio A.
Fabio A.

Reputation: 2724

Why is constructor being elided for this unnamed temporary with side effects with normal initialization but not with braced list initialization?

I wanted to test whether a temporary object would live at least as long as the temporary object that held a const reference to it, so I came up with this example.

#include <iostream>

struct Test {
    Test() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    ~Test() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

struct Holder {
    Holder(const Test& t):m_t(t) {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    ~Holder() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    const Test& m_t;
};

int main() {
    Holder(Test());

    return 0;
}

However, I was quite surprised to see that the compiler had actually optimized out the whole thing, as can be seen on codebolt

But, if I actually give a name to the temporary, by changing the line

Holder(Test());

into

Holder h((Test()));

Then it "magically" works: codebolt.

Plot twist: if I switch to c++11 and use the braced list initialization for the Holder class, then the constructor is not elided no matter whether I give a name to the temporary or not. See codebolt again.

So what's the matter here? I was under the impression that constructors with side effects would never be elided, but I'm obviously missing an important piece of the standard that changed between versions.

Can anybody give me a hint?

Upvotes: 3

Views: 167

Answers (1)

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136256

This is most vexing parse issue: Holder(Test()); declares a function named Test that returns Holder and accepts 0 arguments.

With g++ the following code:

#include <iostream>
#include <type_traits>

struct Test {};
struct Holder { Holder(Test); };

template<class T>
char const* f() { return __PRETTY_FUNCTION__; }

int main() {
    Holder(Test());
    std::cout << f<decltype(&Test)>() << '\n';
}

Outputs:

const char* f() [with T = Holder (*)()]

An easy fix is to use C++11 braces for initialization: Holder(Test{}); or Holder{Test{}};.

Upvotes: 6

Related Questions