简体   繁体   English

包含仅移动类型的类的构造函数是应该通过引用还是通过右值引用来接收仅移动类型?

[英]Should a constructor for a class that contains a move-only type receive the move-only type by reference or by rvalue reference?

I recently started learning about move semantics, and I have been thinking about the following problem for a few days: 我最近开始学习移动语义,几天后我一直在考虑以下问题:

Let's say we have a non-copyable class like the following: 假设我们有一个不可复制的类,如下所示:

class Texture
{
public:

   Texture(unsigned int texID);
   ~Texture();

   Texture(const Texture&) = delete;
   Texture& operator=(const Texture&) = delete;

   Texture(Texture&& rhs);
   Texture& operator=(Texture&& rhs);

   // ...

private:

   unsigned int mTexID;
};

For those wondering, it is common to have such wrapper classes when working with OpenGL. 对于那些想知道的人来说,使用OpenGL时通常会有这样的包装类。 The ID is used to access data that is stored in the GPU, and it is used to tell the GPU to destroy said data, which is done in the destructor of this wrapper class. 该ID用于访问存储在GPU中的数据,它用于告诉GPU销毁所述数据,这是在此包装类的析构函数中完成的。 That is why it is a non-copyable class. 这就是为什么它是一个不可复制的类。

Now let's say we have another non-copyable class like the following: 现在让我们说我们有另一个不可复制的类,如下所示:

class Mesh
{
public:

   // Notice how the constructor receives the vector of Texture objects (a move-only type) by reference
   Mesh(const std::vector<unsigned int>& indices, std::vector<Texture>& textures)
      : mIndices(indices)
      , mTextures(std::move(textures))
   {
      // ...
   }
   ~Mesh();

   Mesh(const Mesh&) = delete;
   Mesh& operator=(const Mesh&) = delete;

   Mesh(Mesh&& rhs);
   Mesh& operator=(Mesh&& rhs);

   // ...

private:

   std::vector<unsigned int> mIndices;
   std::vector<Texture>      mTextures;
};

With the constructor the way it is right now, a client could create a Mesh by doing the following: 使用构造函数的方式,客户端可以通过执行以下操作来创建Mesh

std::vector<unsigned int> indices;
std::vector<Texture> textures;

// ...

Mesh mesh(indices, textures); // Client is unaware that the textures vector has been moved from

My question is whether it would be better if the constructor of the Mesh class was declared like this: 我的问题是,如果Mesh类的构造函数声明为这样会更好:

// Notice how the constructor receives the vector of Texture objects (a move-only type) by rvalue reference
Mesh::Mesh(const std::vector<unsigned int>& indices, std::vector<Texture>&& textures)
   : mIndices(indices)
   , mTextures(std::move(textures))
{
   // ...
}

With this new constructor, the client would be forced to do the following when creating a Mesh object: 使用这个新的构造函数,在创建Mesh对象时,客户端将被强制执行以下操作:

std::vector<unsigned int> indices;
std::vector<Texture> textures;

// ...

Mesh mesh(indices, std::move(textures)); // Client is fully aware that the textures vector has been moved from

It is certainly more typing, but now the user is fully aware that the textures vector has been moved from, and I don't see any performance implications. 它肯定更多的打字,但现在用户完全意识到纹理矢量已被移除,我没有看到任何性能影响。

So I guess my question is: are there are guidelines on what's the best way to receive move-only types that will be moved from? 所以我想我的问题是:是否有关于接收仅移动类型的最佳方法的指导原则是什么? Receiving by reference to const clearly indicates that the type will not be moved from, so how does one do the opposite? 通过引用接收清楚地表明该类型不会被移动,那么如何做相反的事情呢? How does one tell the client that the type will be moved from? 如何告诉客户端类型将被移出?

Using the rvalue reference is clearly superior if the value passed might be a prvalue: 如果传递的值可能是prvalue,那么使用rvalue引用显然更好:

struct A {};
struct B {B(A&);};
struct C {C(A&&);};
A get();
A a;

B b{a};              // OK: a is an lvalue
B b2{get()};         // error: prvalue
C c{a};              // error: lvalue
C c2{std::move(a)};  // OK: xvalue
C c3{get()};         // OK: prvalue

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

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