简体   繁体   English

一个类型是否只能移动,只是因为复制可能很昂贵?

[英]Should a type be move-only, just because copying may be expensive?

I have a type that is copyable, but may be expensive to copy. 我有一个可复制的类型,但复制可能很昂贵。 I have implemented the move constructor and move assignment. 我已经实现了移动构造函数和移动赋值。 But I have performance issues where folks forget to call move() when passing by value. 但是我遇到性能问题,人们忘记在传递值时调用move()。

Is it good C++11 style to remove the copy constructor, and instead provide an explicit copy() method for the rare cases when a copy is actually desired? 是否有良好的C ++ 11样式来删除复制构造函数,而是为实际需要复制的罕见情况提供显式的copy()方法? This is idiomatic in other languages (Ruby, JavaScript) but I don't know of anything in the C++ standard library that prohibits copy purely for performance. 这在其他语言(Ruby,JavaScript)中是惯用的,但我不知道C ++标准库中的任何内容都禁止纯粹为了性能而复制。 For instance, std::vector<> is copyable, while std::unique_ptr<> and std::thread are non copyable for other reasons. 例如,std :: vector <>是可复制的,而std :: unique_ptr <>和std :: thread由于其他原因是不可复制的。

Should a type be move-only, just because copying may be expensive? 一个类型是否只能移动,只是因为复制可能很昂贵?

No . If the semantics of your type is such that copying it is conceptually meaningful, then the correct way to make copying available is to implement a copy constructor, and give the user a chance to adopt standard syntax for invoking it: 如果你的类型的语义是这样的,复制它在概念上是有意义的,那么使复制可用的正确方法是实现一个复制构造函数,并让用户有机会采用标准语法来调用它:

T a;
T a = b;

If people will forget to move from objects they don't want to use anymore... Well, that's their bad: 如果人们忘记从他们不想再使用的物体移动......嗯,这是他们的坏事:

T c = std::move(a); // I'm doing it right (if I no longer need object a);
T d = b; // If I don't need b anymore, I'm doing it wrong.

And if (for any reason) for some functions of yours it is always desirable that the caller provides an object from which it is possible to move, then let the function accept an rvalue reference: 如果(出于任何原因)对于你的某些函数,总是希望调用者提供一个可以从中移动的对象,然后让函数接受一个右值引用:

void foo(my_class&& obj);

my_class a;
foo(a); // ERROR!
foo(std::move(a)); // OK

I would treat the class as non-copyable in signature if copy is sufficiently expensive. 如果副本足够昂贵,我会将该类视为签名中不可复制的。 Semantically things are copyable only if you want them to be, and an expensive copy is a decent reason to decide "no, not copyable". 语义上的东西只有在你想要的时候才是可复制的,而昂贵的副本是决定“不,不可复制”的正当理由。

The ability for something to be copied does not mean it need be implemented in a type that is copyable. 复制某些东西的能力并不意味着它需要以可复制的类型实现。 The implementer of that type gets to decide if it should be semantically copyable. 该类型的实现者可以决定它是否应该是语义可复制的。

I wouldn't call the operation that produced an expensive copy "copy", but rather "clone" or "duplicate". 我不会将产生昂贵副本“副本”的操作称为“克隆”或“重复”。

For a way you might do this: 对于某种方式,您可以这样做:

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

This is somewhat similar to the ways you could have written std::move like semantics prior to the existence of rvalue references, with similar downsides to such techniques, namely that the language itself has no idea what shenanigans you are up to. 这有点类似于你在rvalue引用存在之前编写std::move语义的方式,这类技术有类似的缺点,即语言本身不知道你要做什么恶作剧。

It has the advantage that the syntax of the above do_copy is similar to the syntax of std::move , and it allows you to use traditional expressions without having to create trivial instances of Foo then construct a copy of another variable etc. 它的优点是上面的do_copy的语法类似于std::move的语法,它允许你使用传统的表达式,而不必创建Foo简单实例,然后构造另一个变量的副本等。

If the situations where we want to treat it as copyable are common (if to be avoided), I'd write a copy-wrapper around the class that knows about the duplicate method. 如果我们想将它视为可复制的情况很常见(如果要避免),我会在知道duplicate方法的类周围编写一个copy-wrapper。

No. If the type is copyable then the type is copyable. 不可以。如果类型是可复制的,那么类型是可复制的。 This means its copy constructor is available and works . 这意味着它的复制构造函数可用并且有效 It doesn't mean there's some member function whose name looks like the characters c , o , p and y in sequence, that does "sort of nearly a similar thing". 这并不意味着有一些成员函数,其名称看起来像序列中的字符copy ,它们“几乎是类似的东西”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM