[英]reusing the copy-and-swap idiom
我正在尝试将复制和交换习语放入可重用的混合中:
template<typename Derived>
struct copy_and_swap
{
Derived& operator=(Derived copy)
{
Derived* derived = static_cast<Derived*>(this);
derived->swap(copy);
return *derived;
}
};
我打算通过 CRTP 将其混合:
struct Foo : copy_and_swap<Foo>
{
Foo()
{
std::cout << "default\n";
}
Foo(const Foo& other)
{
std::cout << "copy\n";
}
void swap(Foo& other)
{
std::cout << "swap\n";
}
};
但是,一个简单的测试表明它不起作用:
Foo x;
Foo y;
x = y;
这只会打印两次“default”,既不打印“copy”也不打印“swap”。 我在这里想念什么?
这个:
Derived& operator=(Derived copy)
没有为基础 class 声明复制赋值运算符(它的签名错误)。 所以Foo
中默认生成的赋值运算符不会使用这个运算符。
记住 12.8:
用户声明的复制赋值运算符 X::operator= 是 class X 的非静态非模板成员 function,其中只有一个类型为 X、X&、const X&、volatile X& 或 const volatile X& 的参数。)重载赋值运算符必须声明为只有一个参数; 见 13.5.3。 ] [注意:可以为 class 声明一种以上形式的复制赋值运算符。 ] [注意:如果 class X 仅具有带有 X& 类型参数的复制赋值运算符,则无法将 const X 类型的表达式分配给 X 类型的 object。
编辑不要这样做(你能明白为什么吗?):
你可以做:
template<typename Derived> struct copy_and_swap { void operator=(const copy_and_swap& copy) { Derived copy(static_cast<const Derived&>(copy)); copy.swap(static_cast<Derived&>(*this)); } };
但是你失去了潜在的复制省略优化。
实际上,这将分配两次派生类的成员:一次通过copy_and_swap<Derived>
赋值运算符,一次通过派生类生成的赋值运算符。 要纠正这种情况,您必须这样做(并且不要忘记这样做):
struct Foo : copy_and_swap<Foo>
{
Foo& operator=(const Foo& x)
{
static_cast<copy_and_swap<Foo>&>(*this) = x;
return *this;
}
private:
// Some stateful members here
}
故事的寓意:不要为复制和交换习语写 CRTP class 。
如果 memory 正确服务,则不能将赋值运算符作为特殊情况继承。 我相信他们可以在需要时明确using
'd in。
此外,请注意过度使用复制和交换。 它会产生不理想的结果,其中原始文件具有可重复用于制作副本的资源,例如容器。 安全得到保证,但最佳性能却没有。
编译器会自动为 Foo 生成一个复制赋值运算符,因为没有。 如果你添加一个
using copy_and_swap<Foo>::operator=;
到 Foo 你会看到一个错误告诉你关于 g++ 的歧义。
也许你可以重写它,使它看起来像这样:
template<class Derived>
struct CopySwap
{
Dervied &operator=(Derived const &other)
{
return AssignImpl(other);
}
Derived &operator=(Dervied &&other)
{
return AssignImpl(std::move(other));
}
private:
Derived &AssignImpl(Derived other)
{
auto self(static_cast<Derived*>(this));
self->swap(other);
return *self;
}
};
它可能会全部内联,并且可能不会比原始代码慢。
恐怕这是一个需要宏的领域,因为关于自动生成的复制和赋值运算符的复杂规则。
无论您做什么,您都处于以下两种情况之一:
因此,下一个问题是:自动化这样的写作值得吗?
Copy-And-Swap 仅用于非常特定的类。 我不认为这是值得的。
这并不能真正回答问题( @Alexandre C. 已经回答了),但是如果你反转 inheritance,你可以让它工作:
template<typename Base>
struct copy_and_swap : Base
{
copy_and_swap& operator=(copy_and_swap copy)
{
swap(copy);
return *this;
}
};
struct Foo_
{
Foo_()
{
std::cout << "default\n";
}
Foo_(const Foo_& other)
{
std::cout << "copy\n";
}
void swap(Foo_& other)
{
std::cout << "swap\n";
}
};
typedef copy_and_swap<Foo_> Foo;
int main()
{
Foo x;
Foo y;
x = y;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.