尝试push_back移动ctor删除左值,不编译

时间:2013-12-28 20:46:59

标签: c++11

#include <iostream>
#include <type_traits>
#include <vector>

struct Foo 
{  
    Foo( ) = default;
    Foo(Foo&& f) = delete;
    Foo(const Foo&) = default;
};

int main()
{  
    std::vector<Foo> v;
    Foo f;
    v.push_back(f);
    std::cin.ignore(); 
}

// error C2280: 'Foo::Foo(Foo &&)' : attempting to reference a deleted function 

这种行为是否标准(我在msvc2013 11月CTP下)?

我想在这里,只会调用复制构造函数。

1 个答案:

答案 0 :(得分:3)

我同意这看起来像个错误。话虽如此,你几乎肯定不想以这种方式编码Foo

你上面说的是:

  

我不想从左值移动或复制 Foo

您无法从工厂函数返回此类Foo。虽然您可以将push_back改为vector<Foo>,但您不能insert将其合并为一个。这是一种非常脆弱的类型,具有令人惊讶的行为。

就目前而言,你不能插入vector可能并不太令人惊讶,因为Foo根本没有可访问的赋值运算符:删除了复制赋值运算符,并且移动了赋值运算符根本不存在。让我们继续上面的设计理念,并给出Foo赋值运算符:

struct Foo 
{  
    Foo( ) = default;
    Foo(Foo&& f) = delete;
    Foo& operator=(Foo&& f) = delete;
    Foo(const Foo&) = default;
    Foo& operator=(const Foo&) = default;
};

现在可以将其插入vector<Foo>吗?

不,你还是不行。甚至不在最后:

v.insert(v.end(), f);

In file included from test.cpp:1:
In file included from ../libcxx/include/iostream:38:
In file included from ../libcxx/include/ios:216:
In file included from ../libcxx/include/__locale:15:
In file included from ../libcxx/include/string:439:
In file included from ../libcxx/include/algorithm:627:
../libcxx/include/memory:1641:31: error: call to deleted constructor of 'Foo'
            ::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
                              ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../libcxx/include/memory:1568:18: note: in instantiation of function template specialization 'std::__1::allocator<Foo>::construct<Foo, Foo>' requested here
            {__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
                 ^
../libcxx/include/memory:1449:14: note: in instantiation of function template specialization 'std::__1::allocator_traits<std::__1::allocator<Foo> >::__construct<Foo, Foo>' requested here
            {__construct(__has_construct<allocator_type, pointer, _Args...>(),
             ^
../libcxx/include/vector:1651:25: note: in instantiation of function template specialization 'std::__1::allocator_traits<std::__1::allocator<Foo> >::construct<Foo, Foo>' requested here
        __alloc_traits::construct(this->__alloc(),
                        ^
../libcxx/include/vector:1677:13: note: in instantiation of member function 'std::__1::vector<Foo, std::__1::allocator<Foo> >::__move_range' requested here
            __move_range(__p, this->__end_, __p + 1);
            ^
test.cpp:25:7: note: in instantiation of member function 'std::__1::vector<Foo, std::__1::allocator<Foo> >::insert' requested here
    v.insert(v.end(), f);
      ^
test.cpp:9:5: note: 'Foo' has been explicitly marked deleted here
    Foo(Foo&& f) = delete;
    ^
1 error generated.

insert期间,沿着逻辑的一个分支,vector将尝试沿现有缓冲区移动元素以尝试在中间创建“洞”。即使在最后插入时也不会执行此分支,但直到运行时才会发现该事实。

如果你不希望你的类型具有移动语义,那么这样做的方法是明确地给它复制语义(使用= default很好),并且给它移动语义,删除或其他:

struct Foo 
{  
    Foo( ) = default;
    Foo(const Foo&) = default;
    Foo& operator=(const Foo&) = default;
};

现在您可以将其插入vector<Foo>。您可以从工厂功能返回它。它没有移动成员,甚至没有隐含的移动成员。如果从右值复制此值,则使用复制成员而不是(不存在的)移动成员。

对于奖励积分,这适用于MSVC ++错误。

已删除的移动成员与不存在的移动成员​​之间存在巨大差异。

已删除的移动成员会吸引rvalue参数,如果它们绑定,则会导致编译时错误。不存在的移动成员根本不会引起任何争论。因此,可以通过重载决策选择其他重载,例如复制成员。 rvalues特别会被删除的移动成员(通常容易引起混淆)抓住。但是由于不存在移动成员,rvalues可以自由绑定到(传统的const&)副本成员。

总结:

  

从不 1 指定已删除的移动成员。

1)“从不”是夸大其词。但如果您发现自己编写了已删除的移动成员编码,或者在其他人的代码中查看它们,您应该问自己:为什么这些在这里?!这个程序员是否熟悉移动语义的每个方面?程序员能否解释为什么删除的移动语义存在于:“我不想使用移动语义?”如果没有,请删除已删除的移动语义。