如果副本足够昂贵,我会将该类视为不可复制的签名。从语义上讲,只有您希望它们是可复制的,并且昂贵的副本是决定“不,不可复制”的正当理由。
可以复制的能力并不意味着它需要以可复制的类型实现。该类型的实现者可以决定它是否应该在语义上是可复制的。
我不会将产生昂贵副本的操作称为“复制”,而是将其称为“克隆”或“复制”。
对于一种方法,您可以这样做:
#include <utility>
template<typename T>
struct DoCopy {
T const& t;
DoCopy( T const& t_ ):t(t_) {}
};
template<typename T>
DoCopy<T> do_copy( T const& t ) {
return t;
}
struct Foo {
struct ExpensiveToCopy {
int _[100000000];
};
ExpensiveToCopy* data;
Foo():data(new ExpensiveToCopy()) {}
~Foo(){ delete data; }
Foo(Foo&& o):data(o.data) { o.data = nullptr; }
Foo& operator=(Foo&& o) { data=o.data; o.data=nullptr; return *this; }
Foo& operator=(DoCopy<Foo> o) {
delete data;
if (o.t.data) {
data=new ExpensiveToCopy(*o.t.data);
} else {
data=new ExpensiveToCopy();
}
return *this;
}
Foo( DoCopy<Foo> cp ):data(cp.t.data?new ExpensiveToCopy( *cp.t.data ):new ExpensiveToCopy() ) {};
};
int main() {
Foo one;
// Foo two = one; // illegal
Foo three = std::move(one); // legal
Foo four;
Foo five = do_copy(three);
four = std::move(three);
five = do_copy(four);
}
这有点类似于在右值引用存在之前您可以编写std::move
类似语义的方式,与此类技术具有相似的缺点,即语言本身不知道您在做什么恶作剧。
它的优点是上面do_copy
的语法类似于 的语法std::move
,并且它允许您使用传统表达式,而无需创建琐碎的实例Foo
then 构造另一个变量的副本等。
如果我们希望将其视为可复制的情况很常见(如果要避免),我会在知道该duplicate
方法的类周围编写一个复制包装器。