0

If a constructor B() is called to create a temporary object, e.g. in a call f(B().name()), will the construction of B always carried out, or are compilers permitted and able to optimize out unnecessary allocations that go along with object creation?

Specific example:

class A {
 public:
    virtual std::string name() = 0;
};

class B : public A {
 public:
    std::string name() final { return "MyClassName"; }
    // cannot be made static, because at some places we need
    // the polymorphic call from A-pointers
 private:
    int data;
    ...
    // members that require heap allocation
};

int main() {
    std::cout << "Class B is named " << B().name() << std::endl;
}

Will the last statement actually create an instance of B, including allocation of storage for B().data?

trincot
  • 317,000
  • 35
  • 244
  • 286
Joachim W
  • 7,290
  • 5
  • 31
  • 59
  • Non-static member functions *always* needs an object to be called on. Even if you don't use the object itself (the `this` pointer) in the function, the object still needs to exist. – Some programmer dude Aug 05 '20 at 08:42
  • 2
    Real question is "why does it matter to OP"? Maybe elaborate on your use case or concerns. – Louis Go Aug 05 '20 at 08:50
  • Do you care about whether it's legal _in general_, or whether some specific compiler will actually optimize it out in this specific code? Because for the latter, you could just check. – Useless Aug 05 '20 at 09:01
  • In common constructors do not have any return type not even void. If I add const to your function prototype (because it is), one cannot compile the code. Is it legal to do B().name() ?? – StephanH Aug 05 '20 at 09:04
  • By the way, you return a reference to a temporary from a function. I also get a runtime error at B().name(). However, I wonder how this even could compile in the first place. Use a static variable in the function name(). – StephanH Aug 05 '20 at 09:08
  • The simply answer is: If nothing of your functions, even the constructor, has side effects everything can be moved out. If you need an object but do not access objects data, which means you do not really need the object itself, even the allocation of the object can be dropped. But as always: It is totally unimportant what can be done or not, simply take a look on the generated code! And also this is mostly irrelevant as long you have no memory or speed requirements. And in the last case: Simply measure, measure, measure! – Klaus Aug 05 '20 at 09:13
  • @StephanH: Thanks, edited example to avoid this distraction. – Joachim W Aug 05 '20 at 09:42

1 Answers1

4

A compiler may completely remove a call to constructor just like any other function. While an instance of B is necessary for the code to be semantically correct, it doesn't mean that after compilation it needs to exist (as a piece of memory). Also remember that a call to constructor is not the same as allocation.

So as far as I understand, the question is whether heap allocation (as in usage of new/delete operators) can be removed by the compiler as well? Typically compilers have a hard time with side effect operations. However allocations seems to be an exception: Can the compiler optimize from heap to stack allocation? as long as new/delete operators are not overloaded.

A concerete example is this:

class A {
 public:
    virtual int get() = 0;
    virtual ~A() {};
};

class B : public A {
 public:
    int get() { return 5; }
    ~B() {
        delete ptr;
    }
 private:
    int data;
    int* ptr = new int;
};

int foo() {
    return B().get();
}

which under all the compilers I've tried (gcc and clang, X64, with full optimizations on) produces simple

foo():
        mov     eax, 5
        ret

with no allocations at all.

freakish
  • 54,167
  • 9
  • 132
  • 169
  • Not with MSVC. Even for the others, I'm not quite sure how they're omitting the call to `operator new` since that could be defined in another translation unit. As one of the answers you linked to says, a compiler is allowed to elide multiple calls to `operator new` into one, but must still ultimately call it. Maybe the compiler writers feel that it's worth being slightly non-conformant for the speed improvement. – Arthur Tacca Aug 05 '20 at 09:15
  • @ArthurTacca `operator new` has to be called if it is a non-replacable global function (i.e. overload?). Otherwise it can be totally removed. Or at least this is how I understand the linked post. Also I didn't check MSVC, I've updated the answer about compilers info. – freakish Aug 05 '20 at 09:20