#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下)?
我想在这里,只会调用复制构造函数。
答案 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)“从不”是夸大其词。但如果您发现自己编写了已删除的移动成员编码,或者在其他人的代码中查看它们,您应该问自己:为什么这些在这里?!这个程序员是否熟悉移动语义的每个方面?程序员能否解释为什么删除的移动语义存在于:“我不想使用移动语义?”如果没有,请删除已删除的移动语义。