Why is private move constructor allowed while initalizing via static method?

59 Views Asked by At

Simplified code snippet is:

class A {
  public:
    ~A();
    static A create();

  private:
    A() = default;
    A(A&&) = default;
    NonCopyable n;
};

A A::create() {
  A a;
  return a;
}

int main(int argc, char* argv[]) {
  auto a = A::create();
  return 0;
}

Please also see my live example (which shows different compilers' behavior).

In the end, I'm wondering why does auto a = A::create(); compile without errors using newer compilers [gcc >= 7.1] (which part of the C++17 standard is relevant here?), given that:

  1. We have a non-copyable member NonCopyable n;, so default copy constructor would be ill-formed.
  2. It's an NRVO here since A a; return a; so copy elision is not guaranteed by the standard.
  3. Move constructor A(A&&) is marked private.
  4. Optimizations were off -O0 for testing.

My suspicion is that move constructor is being "validated" by the compiler at return a;; since this is a member function of A it passes the validation. Even if the suspicion is correct, I'm not sure if this is standard-compliant.

1

There are 1 best solutions below

0
Florian Weimer On

I believe this is a consequence of P0135: Wording for guaranteed copy elision through simplified value categories, specifically the change to [dcl.init]:

  • If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object. [Example: T x = T(T(T())); calls the T default constructor to initialize x. — end example]

As a result, this behavior is not dependent on copy elision of return values or the availability of move constructors.