看完这个问题最近通过@Mehrdad在哪些类应作出不可移动的,因此不可复制的 ,我开始如果有使用案例可以复制,但不动的一类疑惑。 从技术上讲,这是可能的:
struct S
{
S() { }
S(S const& s) { }
S(S&&) = delete;
};
S foo()
{
S s1;
S s2(s1); // OK (copyable)
return s1; // ERROR! (non-movable)
}
虽然S
有一个拷贝构造函数,它显然不模型的CopyConstructible
概念,因为那是反过来的细化MoveConstructible
概念,这需要一个(非删除)移动构造函数的存在(见第17.6.3.1/2,表21)。
是否有任何使用案例的类型像S
上面,这是复制,但是不能 CopyConstructible
和不可移动的 ? 如果不是, 为什么不禁止声明拷贝构造函数和在同一类删除移动构造函数?
假设你有一个类,它是没有更便宜的移动比它复制(也许它包含std::array
一个POD类型)。
在功能上,你“应该”让MoveConstructible使S x = std::move(y);
行为类似于S x = y;
,这就是为什么拷贝构造是MoveConstructible的子概念。 通常,如果你没有声明构造函数所有,这“只是工作”。
在实践中,我想你可能需要暂时禁用移动构造函数,以检测是否有在你的程序出现更高效的比它确实是,通过移动的情况下,任何代码S
。 对我来说,似乎是过分了禁止这一点。 这不是标准的职责就是实施良好的界面设计完成的代码:-)
我目前所知,没有用例删除的移动构造函数/赋值。 如果做得不小心就会无谓地防止类型从一个工厂函数返回或放入std::vector
。
然而此举删除成员合法但以防万一有人可能会发现他们使用。 作为一个比喻,我知道没有使用的const&&
多年。 人们问我,如果我们不应该只是取缔它。 但最终一些用例都出现了之后,我们得到了与该功能足够的经验。 相同的情况也可能会被删除的举动成员发生,但据我所知还没有。
我不认为有可以是任何合理的类,它会阻止的举动 ,但允许复制 。 它是由非常同一主题明确,此举是副本只是一种有效的方法,当你不需要原来的对象了。
今天我一直在寻找在这个非常的问题,因为我们已经从VS2005移植一些代码到VS2010和开始看到内存损坏。 它原来是因为一个优化(避免拷贝做地图查找时)并没有转化为C与移动语义++ 11。
class CDeepCopy
{
protected:
char* m_pStr;
size_t m_length;
void clone( size_t length, const char* pStr )
{
m_length = length;
m_pStr = new char [m_length+1];
for ( size_t i = 0; i < length; ++i )
{
m_pStr[i] = pStr[i];
}
m_pStr[length] = '\0';
}
public:
CDeepCopy() : m_pStr( nullptr ), m_length( 0 )
{
}
CDeepCopy( const std::string& str )
{
clone( str.length(), str.c_str() );
}
CDeepCopy( const CDeepCopy& rhs )
{
clone( rhs.m_length, rhs.m_pStr );
}
CDeepCopy& operator=( const CDeepCopy& rhs )
{
if (this == &rhs)
return *this;
clone( rhs.m_length, rhs.m_pStr );
return *this;
}
bool operator<( const CDeepCopy& rhs ) const
{
if (m_length < rhs.m_length)
return true;
else if (rhs.m_length < m_length)
return false;
return strcmp( m_pStr, rhs.m_pStr ) < 0;
}
virtual ~CDeepCopy()
{
delete [] m_pStr;
}
};
class CShallowCopy : public CDeepCopy
{
public:
CShallowCopy( const std::string& str ) : CDeepCopy()
{
m_pStr = const_cast<char*>(str.c_str());
m_length = str.length();
}
~CShallowCopy()
{
m_pStr = nullptr;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
std::map<CDeepCopy, int> entries;
std::string hello( "Hello" );
CDeepCopy key( hello );
entries[key] = 1;
// Named variable - ok
CShallowCopy key2( hello );
entries[key2] = 2;
// Unnamed variable - Oops, calls CDeepCopy( CDeepCopy&& )
entries[ CShallowCopy( hello ) ] = 3;
return 0;
}
上下文是我们希望避免在不必要的情况下堆分配,地图键已经存在 - 因此,CShallowCopy类是用来做初始查找,那么它会被复制,如果这是一个插入。 问题是,这种方法并不能与移动语义工作。
这取决于你如何定义移动操作的语义为你的类型。 如果此举仅仅是指通过优化副本然后资源窃取答案很可能是否定的。 但答案也许是肯定的,如果此举意味着在由移动垃圾收集器或其他自定义的内存管理方案使用的意义上的“重新定位”。
考虑定位于特定的街道地址的房子的真实世界的例子。 一个可以定义的房子是用完全相同的蓝图又建了房子的副本,但位于另一个地址。 在这些方面,我们可以去说房子不能移动,因为有可能是人通过其地址引用它。 翻译专业术语,移动操作可能不适用于有入三分球结构是可能的。
我能想象有点扭曲实施的信号/插槽库,允许被复制的信号的对象,但不允许他们移动。
免责声明:某些C ++纯粹主义者会指出,STL(因此标准)定义了移动操作是什么,这是不符合描述的东西在这里我,所以我不会有异议。