3

This is my class

class A {
public:
    A(int);
    A(A const&) = delete;
    A& operator=(const A& a) = delete;
// etc.
};

In another file I can call the constructor like this:

auto foo = A(123);

That resolves to the copy constructor and not the one I expect, why?

error C2280: 'mynamspace::A::A(const mynamespace::A &)': attempting to reference a deleted function
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Allie
  • 57
  • 2
  • I don't understand your question. `auto foo = A(123)` obviously called the copy constructor. – Yves Jul 27 '20 at 01:48
  • @Yves Well, in modern C++, it *doesn't* call the copy constructor. It hasn't since C++17. – HTNW Jul 27 '20 at 01:50
  • Maybe it depends on the version of C++ ... GCC 4.7.1 is the first one that produces that error. And then when Using -std=c++17 arrives with GCC 7.1 it works. – Jerry Jeremiah Jul 27 '20 at 01:52
  • Re: your edit: it's trying to use *both* constructors. It wants to use the not-deleted one (during `A(123)`) followed by the deleted one (during `auto foo = ...`). Your code says to first construct a temporary object, then to copy it into `foo` (at least, that's what it says before C++17). The assignment operator is irrelevant (also, I think you dropped the `=` in `operator=`). In C++14 and before, you need to explicitly say you want to directly construct `foo` from `123`: `A foo(123)`. In C++17, the meaning of your code changes and it does directly construct `foo` from`123`. – HTNW Jul 27 '20 at 02:09

1 Answers1

6

auto foo = A(123); performs copy initialization, in concept, A(123) constructs a temporary A by A::A(int) firstly, then foo is copy-initialized from the temporary by the copy constructor (PS A doesn't have move constructor). Before C++17 the code is ill-formed because the copy constructor is marked as delete.

Since C++17 the copy construction is elided completely because of mandatory copy elision and the code is well-formed.

(Since C++17) First, if T is a class type and the initializer is a prvalue expression whose cv-unqualified type is the same class as T, the initializer expression itself, rather than a temporary materialized from it, is used to initialize the destination object: see copy elision

and

(emphais mine)

(Since C++17) Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:

Note that before C++17, copy elision is an optimization, even it might be performed the copy/move constructor still must be present and accessible.

This is an optimization: even when it takes place and the copy/move (since C++11) constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed:

You can upgrade your compiler to support C++17, or use direct initialization (to remove the requirement of usage of copy constructor).

A foo(123);
A foo{123};
songyuanyao
  • 169,198
  • 16
  • 310
  • 405