让我们说我有一个只移动的类型。我们停止现有的默认提供的构造函数,但Rvalue引用引入了一个新的" flavor"我们可以用于签名的移动版本:
class CantCopyMe
{
private:
CantCopyMe (CantCopyMe const & other) = delete;
CantCopyMe & operator= (CantCopyMe const & other) = delete;
public:
CantCopyMe (CantCopyMe && other) {
/* ... */
}
CantCopyMe & operator= (CantCopyMe && other) {
/* ... */
}
};
我最近认为你总是应该通过Rvalue引用传递可移动类型。现在它看起来只有非常特殊的情况需要这样做......就像这两个。如果你把它们放在任何地方,事情似乎大部分时间都有效,但我刚发现一个编译器没有运行转移所有权的代码部分。
(这是一种情况,例如将带有std::move
的变量中保存的唯一指针传递给采用unique_ptr<foo> &&
参数的内容...但注意到调用点上的变量没有被取消将参数更改为unique_ptr<foo>
修正了它并且它已被正确排除,从而防止了双重删除。: - /我没有理解为什么这个看起来很糟糕在其他地方工作,但吸烟枪是第一次工作但不是随后的电话。)
我确定这是一个很好的理由,你们中的许多人都可以得到充分的总结。与此同时,我开始像一个好的cargo-cult programmer去除&amp;&amp; s。
但是,如果你正在写一个模板化的课程,它看起来像这样呢?
template <class FooType>
class CantCopyMe
{
private:
CantCopyMe (CantCopyMe const & other) = delete;
CantCopyMe & operator= (CantCopyMe const & other) = delete;
public:
template<class OtherFooType>
CantCopyMe (CantCopyMe<OtherFooType> && other) {
/* ... */
}
template<class OtherFooType>
CantCopyMe & operator= (CantCopyMe<OtherFooType> && other) {
/* ... */
}
};
由于某些原因这是不好的做法,当OtherFooType和FooType不相同时你应该单独分解......然后它只是按值传递?
template <class FooType>
class CantCopyMe
{
private:
CantCopyMe (CantCopyMe const & other) = delete;
CantCopyMe & operator= (CantCopyMe const & other) = delete;
public:
CantCopyMe (CantCopyMe && other) {
/* ... */
}
CantCopyMe & operator= (CantCopyMe && other) {
/* ... */
}
template<class OtherFooType>
CantCopyMe (CantCopyMe<OtherFooType> other) {
/* ... */
}
template<class OtherFooType>
CantCopyMe & operator= (CantCopyMe<OtherFooType> other) {
/* ... */
}
};
答案 0 :(得分:2)
我认为可能出乎意料的原因有一个简单的答案:
复制/移动构造函数或赋值运算符从不模板(特化)。例如。 [class.copy] / 2
如果第一个参数的类型为
X
,X&
,const X&
或volatile X&
,则类const volatile X&
的非模板构造函数是一个复制构造函数,并且没有其他参数,或者所有其他参数都有默认参数。
此外,脚注122说:
因为模板赋值运算符或赋予rvalue引用参数的赋值运算符永远不是复制赋值运算符,所以这种赋值运算符的存在不会抑制复制赋值运算符的隐式声明。此类赋值运算符与其他赋值运算符(包括复制赋值运算符)一起参与重载解析,如果选择,则将用于分配对象。
示例:
#include <iostream>
#include <utility>
template<class T>
struct X
{
X() {}
template<class U>
X(X<U>&&)
{
std::cout << "template \"move\" ctor\n";
}
template<class U>
X& operator= (X<U>&&)
{
std::cout << "template \"move\" assignment-op\n";
return *this;
}
};
int main()
{
X<int> x; // no output
X<int> y(x); // no output
y = std::move(x); // no output
X<double> z( std::move(x) ); // output
y = std::move(z); // output
}
在此示例中,使用了隐式声明的移动构造函数和移动赋值运算符。
因此,如果您未声明非模板移动ctor并移动赋值运算符,则可能会隐式声明它们。它们没有被隐含地声明,例如对于move assignment-op,如果你有一个用户声明的dtor;有关详细信息,请参阅[class.copy] / 11和[class.copy] / 20。
示例:在上面的示例中添加dtor:
#include <iostream>
#include <utility>
template<class T>
struct X
{
X() {}
~X() {}
template<class U>
X(X<U>&&)
{
std::cout << "template \"move\" ctor\n";
}
template<class U>
X& operator= (X<U>&&)
{
std::cout << "template \"move\" assignment-op\n";
return *this;
}
};
int main()
{
X<int> x; // no output
X<int> y(x); // no output
y = std::move(x); // output
X<double> z( std::move(x) ); // output
y = std::move(z); // output
}
这里,第一个移动赋值y = std::move(x);
调用赋值运算符模板的特化,因为没有隐式声明的移动赋值运算符。