5

I cannot compile the attached project because I deleted the move constructors.

Is this the expected behavior? Why does the compiler need the move constructors if it won't use it?

windows-visual studio 2015 14.0.25431.01 update3

#include <string>
#include <sstream>
#include <vector>

class poo {
public:
  poo() = default;
  poo(poo&&) = delete; //deleted function
  virtual ~poo() = default;

  poo operator +(const poo &a) const {
    poo to_return;
    to_return._s += a._s;
    return to_return;
    //moveconstructors.cpp(14): error C2280: 'poo::poo(poo &&)': attempting to reference a deleted function
  }
private:
  std::string _s;
};

int main(int, char **) {
  poo a;
  return 0;
}

EDIT 1: the same result happens after adding "poo (const poo &) = default;"

EDIT 2: the same result happens with windows-visual studio 2019 16.1.0 preview 2.0

EDIT 3: the same result happens after adding/modifying

  poo(const std::string &s) : _s(s) {
  }
  poo operator +(const poo& a) const {
    return poo(_s + a._s);
  }

EDIT 4: it works fine with vs2019 and /std:c++17

2 Answers2

8
poo(poo&&) = delete;

This line disables the move constructor, yes, but it also deletes the copy constructor.

From class.copy.ctor:

If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted;.

Now, all this wouldn't be a big deal if named return value optimization (NRVO) was guaranteed by the standard, because the compiler could see that your operator+ has a single return of a local variable. In that case, we wouldn't need copy or move constructors; the poo instance would be created and passed to the fn by reference ("under the hood").

Note that in C++17 you can use guaranteed copy elision (RVO) to get around this:

poo(std::string s) : _s(std::move(s)){}

poo operator +(const poo &a) const {
  return poo(_s + a._s);
}

Demo

However, even in C++20, named return value optimization is not yet guaranteed. An implementation is permitted to use a move operation instead.

[class.copy.elision] states:

In the following copy-initialization contexts, a move operation might be used instead of a copy operation:
- If the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression

AndyG
  • 39,700
  • 8
  • 109
  • 143
3

A elision optimisation is permitted here (NRVO) but the object must still be semantically copyable/moveable; by deleting the move constructor, you also deleted the copy constructor, so neither operation is valid and the program is ill-formed.

You can add the copy ctor back:

poo(const poo&) = default;

(And, even if you were using C++17, its guaranteed elision doesn't apply to lvalues.)

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055