Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy/move elision requires explicit definition of copy/move constructors

Consider the following program:

#include <iostream>
#include <utility>

class T {
public:
    T() { printf("address at construction:    %zx\n", (uintptr_t)this); }
    // T(const T&) { printf("copy-constructed\n"); } // helps
    // T(T&&) { printf("move-constructed\n"); }      // helps
    // T(const T&) = default;                        // does not help
    // T(T&&) = default;                             // does not help
};

T f() { return T(); }

int main() {
    T x = f();
    printf("address after construction: %zx\n", (uintptr_t)&x);
    return 0;
}

Compiling with g++ -std=c++17 test.cpp gives the following output (same with clang++):

address at construction:    7ffcc7626857
address after construction: 7ffcc7626887

Based on the C++ reference I would expect the program to output two equal addresses because the copy/move should be guaranteed to be elided (at least in C++17).

If I explicitly define either the copy or the move constructor or both (see commented out lines in the example), the program gives the expected output (even with C++11):

address at construction:    7ffff4be4547
address after construction: 7ffff4be4547

Simply setting the copy/move constructors to default does not help.

The reference explicitly states

[The copy/move constructors] need not be present or accessible

So what am I missing here?

like image 667
Al Gebra Avatar asked Apr 07 '18 07:04

Al Gebra


People also ask

What is the difference between copy constructor and move constructor?

Move constructor moves the resources in the heap, i.e., unlike copy constructors which copy the data of the existing object and assigning it to the new object move constructor just makes the pointer of the declared object to point to the data of temporary object and nulls out the pointer of the temporary objects.

What is copy elision in Javascript?

Copy elision is an optimization implemented by most compilers to prevent extra (potentially expensive) copies in certain situations. It makes returning by value or pass-by-value feasible in practice (restrictions apply).

What is the difference between move constructor and move assignment?

The move assignment operator is different than a move constructor because a move assignment operator is called on an existing object, while a move constructor is called on an object created by the operation. Thereafter, the other object's data is no longer valid.


1 Answers

Because this is a special case where copy elision may not apply.

Quoted from [class.temporary] paragraph 3:

When an object of class type X is passed to or returned from a function, if each copy constructor, move constructor, and destructor of X is either trivial or deleted, and X has at least one non-deleted copy or move constructor, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function's parameter or return object is initialized as if by using the non-deleted trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object). [ Note: This latitude is granted to allow objects of class type to be passed to or returned from functions in registers. — end note ]

like image 156
xskxzr Avatar answered Oct 25 '22 18:10

xskxzr