gpt4 book ai didi

c++ - 模板是否应该为仅移动不同类型的参数创建非右值引用构造函数/赋值?

转载 作者:行者123 更新时间:2023-11-30 04:12:39 31 4
gpt4 key购买 nike

假设我有一个只能移动的类型。我们停止现有的默认提供的构造函数,但 Rvalue 引用引入了一种新的“ flavor ”,我们可以将其用于签名的移动版本:

class CantCopyMe
{
private:
CantCopyMe (CantCopyMe const & other) = delete;
CantCopyMe & operator= (CantCopyMe const & other) = delete;

public:
CantCopyMe (CantCopyMe && other) {
/* ... */
}

CantCopyMe & operator= (CantCopyMe && other) {
/* ... */
}
};

我最近认为您总是应该通过右值引用传递可移动类型。现在看起来只有非常特殊的情况才需要这样做……就像这两个。如果将它们放在任何地方,大多数情况下似乎都能正常工作,但我只发现了一个编译器未运行代码转移所有权部分的情况。

(这是一种情况,比如将保存在 std::move 的变量中的唯一指针传递给采用 unique_ptr<foo> && 参数的东西......但注意到调用站点的变量没有被清空。将参数更改为 unique_ptr<foo> 修复了它并且它被正确地清空,从而防止了双重删除。:-/我还没有分清为什么 this 在它似乎在其他地方工作时却很糟糕,但是确凿的证据是它第一次工作但没有随后的调用。)

我敢肯定这是有充分理由的,你们中的许多人都可以清楚地总结出来。与此同时,我开始像一个好的 cargo-cult programmer 一样四处走动,删除了 &&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) {
/* ... */
}
};

最佳答案

我认为有一个可能出乎意料的简单答案:

复制/移动构造函数或赋值运算符从不是模板(特化)。例如。 [class.copy]/2

A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments.

此外,脚注 122 说:

Because a template assignment operator or an assignment operator taking an rvalue reference parameter is never a copy assignment operator, the presence of such an assignment operator does not suppress the implicit declaration of a copy assignment operator. Such assignment operators participate in overload resolution with other assignment operators, including copy assignment operators, and, if selected, will be used to assign an object.

例子:

#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
}

在此示例中,使用了隐式声明的移动构造函数和移动赋值运算符。


因此,如果您不声明非模板移动构造函数和移动赋值运算符,则可能会隐式声明它们。它们没有隐式声明,例如对于移动赋值操作,如果你有一个用户声明的 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); 调用赋值运算符模板的特化,因为没有隐式声明的移动赋值运算符。

关于c++ - 模板是否应该为仅移动不同类型的参数创建非右值引用构造函数/赋值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19746474/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com