I'm having compiler errors when trying to emplace_back in a vector of non-copyable but movable objects with a subtle inheritance twist, that should to my knowledge not change the problem.
Is this legal C++ and this a Visual Studio 2015 bug, or am I making an obvious mistake ?
#include <vector>
class Base
{
public:
Base() {}
Base(Base&) = delete;
Base& operator= (Base&) = delete;
};
class Test : public Base
{
public:
Test(int i) : m_i(i) {}
Test(Test&&) = default;
Test& operator= (Test&&) = default;
protected:
int m_i;
};
int main(int argc, char *argv[])
{
std::vector<Test> vec;
vec.emplace_back(1);
}
Output :
error C2280: 'Test::Test(Test &)': attempting to reference a deleted function
Without the inheritance, that is with deleted copy constructor in Test and no base class, it compiles correctly.
Somehow, removing the default in the move-constructor make it compile correctly also, but then I have to define the move constructor and I don't want to go there.
Which means this compiles fine :
#include <vector>
class Test
{
public:
Test(int i) : m_i(i) {}
Test(Test&) = delete;
Test& operator= (Test&) = delete;
Test(Test&&) = default;
Test& operator= (Test&&) = default;
protected:
int m_i;
};
int main(int argc, char *argv[])
{
std::vector<Test> vec;
vec.emplace_back(1);
}
Puzzling ?
The compiler is correct in all the cases you described.
When
Testis derived fromBase, its defaulted move constructor is defined as deleted because it tries to moveBase, which cannot be move-constructed.Testis actually not move-constructible in your first example.In your second example, with no base class, there's nothing to prevent the defaulted move constructor from being defined, so
Testbecomes move-constructible.When you provide a definition for the move constructor, it's up to you to handle
Base. If you just writethis will just default-construct a
Baseobject, so the move constructor will compile, but it probably won't do what you want.Baseis not move-constructible because it has a user-declared copy constructor, which prevents the implicit declaration of a move constructor - it has no move constructor at all - and its copy constructor is deleted (it couldn't handle rvalues anyway because it takes a non-const reference).If you make
Basemove-constructible, by adding, for example,then
Testalso becomes move-constructible, and your example will compile.One last piece of the puzzle: since we have declared a move constructor for
Test, why does the error message reference the deleted copy constructor, even in the first case?For an explanation of
std::vector's logic for choosing which constructor to use to copy / move elements during reallocation, see this answer. Looking at the logic that std::move_if_noexcept uses to choose which kind of reference to return, it will be an rvalue reference in our case (whenTis not copy-constructible, the condition is always false). So, we'd still expect the compiler to attempt to call the move constructor.However, one more rule comes into play: a defaulted move constructor that is defined as deleted does not participate in overload resolution.
This is done so that construction from an rvalue can fall back to a copy constructor taking a
constlvalue reference, if available. Note that the rule does not apply when the move constructor is explicitly declared as deleted.