0

In C++, assume C is a class with constructors. When define an instance c of C by

C c = C(args);

does = invoke a copy constructor which takes as an argument the return of C(args) , a call to a constructor?

Or does = not invoke a copy constructor, but associate the name c to the returned object of C(args)?

Thanks.

Tim
  • 1
  • 141
  • 372
  • 590
  • How would you test it? @Dim – Tim Nov 04 '17 at 19:06
  • 2
    @DimChtz Not really, this is a question about standards. – Sergey Kalinichenko Nov 04 '17 at 19:07
  • See [this Q&A](https://stackoverflow.com/q/12953127/335858), it explains that a conforming compiler is allowed to skip it if it wants to, but it's not required to skip the copy constructor. – Sergey Kalinichenko Nov 04 '17 at 19:12
  • In C++17 rules are changing once again so compiler must call `C` constructor only once even if you write something like this `C c = C(C(C(args)));` and copy/move constructors are all deleted. That Q&A from the comment above is outdated. – user7860670 Nov 04 '17 at 19:18

1 Answers1

4

The copy constructor can be invoked, but it can also be optimized away (aka "copy elision") in C++14 and older. But in C++17, the elision is guaranteed.

A difference between C++17 and prior standards is that even if the copy ctor call is elided in C++14 and older, it must still exist, even if it's not called. Here's an example:

struct C {
    C(int) { }
    C(const C&) = delete;
};

int main()
{
    C c = C(1);
}

In C++17, this will compile. In C++14 and older, it will not. Even though the compiler might elide the copy ctor in C++14 too, it is not allowed to accept the code. Since C++17 guarantees the elision, the copy ctor is not required to exist.

Note that this is not related to move semantics. C c = C(1) does not move the temporary into c. We can delete the move constructor too:

struct C {
    C(int) { }
    C(C&&) = delete;
    C(const C&) = delete;
};

And it still compiles in C++17. This is elision, not a move operation.

There is also no assignment going on here. In declarations, the assignment operators are not called to begin with. But we can be paranoid and delete those too for good measure (both the assignment operator and the move assignment operator):

struct C {
    C(int) { }
    C(C&&) = delete;
    C(const C&) = delete;
    C& operator=(const C&) = delete;
    C& operator=(C&&) = delete;
};

And it will still compile in C++17.

Nikos C.
  • 50,738
  • 9
  • 71
  • 96