包含仅移动类型的 class 的构造函数应该通过引用还是通过右值引用接收仅移动类型?
Should a constructor for a class that contains a move-only type receive the move-only type by reference or by rvalue reference?
最近开始学习move语义,这几天一直在思考以下问题:
假设我们有一个不可复制的 class,如下所示:
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;
};
对于那些想知道的人,在使用 OpenGL 时通常会有这样的包装器 classes。 ID 用于访问存储在 GPU 中的数据,并用于告诉 GPU 销毁所述数据,这是在这个包装器 class 的析构函数中完成的。这就是为什么它是不可复制的 class.
现在假设我们有另一个不可复制的 class,如下所示:
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;
};
使用现在的构造函数,客户可以通过执行以下操作来创建 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
我的问题是,如果 Mesh
class 的构造函数这样声明是否会更好:
// 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))
{
// ...
}
使用这个新构造函数,客户端在创建 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
这肯定需要更多的输入,但现在用户完全知道纹理矢量已被移走,我没有看到任何性能影响。
所以我想我的问题是:是否有关于接收将从中移动的仅移动类型的最佳方式的指南?通过引用接收 const 显然表明类型不会被移动,那么如何做相反的事情呢?如何告诉客户类型将被移出?
如果传递的值可能是纯右值,则使用右值引用显然更优越:
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
最近开始学习move语义,这几天一直在思考以下问题:
假设我们有一个不可复制的 class,如下所示:
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;
};
对于那些想知道的人,在使用 OpenGL 时通常会有这样的包装器 classes。 ID 用于访问存储在 GPU 中的数据,并用于告诉 GPU 销毁所述数据,这是在这个包装器 class 的析构函数中完成的。这就是为什么它是不可复制的 class.
现在假设我们有另一个不可复制的 class,如下所示:
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;
};
使用现在的构造函数,客户可以通过执行以下操作来创建 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
我的问题是,如果 Mesh
class 的构造函数这样声明是否会更好:
// 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))
{
// ...
}
使用这个新构造函数,客户端在创建 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
这肯定需要更多的输入,但现在用户完全知道纹理矢量已被移走,我没有看到任何性能影响。
所以我想我的问题是:是否有关于接收将从中移动的仅移动类型的最佳方式的指南?通过引用接收 const 显然表明类型不会被移动,那么如何做相反的事情呢?如何告诉客户类型将被移出?
如果传递的值可能是纯右值,则使用右值引用显然更优越:
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