命名右值引用
Named rvalue references
请原谅我在这个话题上不够清晰。我正在尝试创建用于将大 class 插入向量的函数。在这个例子中,我使用整数向量作为大 class.
#include <vector>
#include <iostream>
using namespace std;
vector<vector<int>> vectorOfVectors;
void fn(const vector<int> &v) {
vectorOfVectors.push_back(v);
}
void fn(vector<int> &&v) {
vectorOfVectors.push_back(std::move(v));
}
int main() {
vector<int> a({1});
const vector<int> b({2});
fn(std::move(a));
fn(b);
cout<<b[0];
}
显然,我希望尽可能不进行复制。我的问题:
- 这段代码做对了吗?
- 有更好的方法吗?
- 对于使用自定义 classes 的相同方法,我是否需要定义移动构造函数?
Does this code do the right thing?
是的。 C++11 正是出于这个原因添加了 std::vector::push_back(T&&)
。
Is there a better way of doing this?
你的 fn(const vector<int> &v)
和 fn(vector<int> &&v)
都在做同样的事情,将参数 v
推到 vectorOfVectors
的末尾。您可以使用一个使用完美转发的函数模板来代替您的两个 fn
函数。
template<typename T>
void fn(T &&v) {
vectorOfVectors.push_back(std::forward<T>(v));
}
这要归功于 C++11 reference collapsing rules 和 std::forward
。在 v
是左值的情况下,模板类型 T
变为 vector<int>&
,而在 v
是右值的情况下,模板类型 vector<int>&&
变为 vector<int>&
。引用折叠规则意味着 vector<int>& &&
变为 vector<int>&
而 vector<int>&& &&
变为 vector<int>&&
。这正是你想要的,调用 push_back
的版本,在左值的情况下执行复制,而在右值的情况下调用移动的版本。
一个缺点是,当您出错时,这有时会导致有趣的诊断。 ("Interesting" 这里表示来自 g++ 或 clang++ 的数百行高深莫测的诊断文本)。另一个缺点是模板可能导致 "converters gone wild".
的情况
For the same approach to work with custom classes, do I need to define move constructors?
不一定。如果 class 没有声明用户定义的析构函数、复制构造函数、复制赋值运算符或移动赋值运算符,您将获得一个隐式声明的移动构造函数。如果 class 具有不可移动的数据成员或派生自无法移动或删除的 class,则隐式声明的移动构造函数将被定义为已删除。
对我来说,这有点太难记了。我不知道这是一个好习惯还是一个坏习惯,但我已经开始使用 Foo(const Foo&)=default
,对五个函数的其他规则使用类似的声明。在许多情况下,我还将构造函数限定为 explicit
以避免 "converters gone wild" 问题。
- 它确实避免了复制
a
。
是的。使用 push_back
意味着您必须至少构造两个对象,而 emplace_back
和完美转发可以做更少的工作。
template<typename... Ts>
auto fn(Ts &&...ts)
-> decltype(vectorOfVectors.emplace_back(std::forward<Ts>(ts)...), void())
{
vectorOfVectors.emplace_back(std::forward<Ts>(ts)...);
}
3。只要您使用 push_back
,您就需要 类 可移动构造以避免复制。如果您可以获得默认定义,则不一定需要自己定义移动构造函数。
请原谅我在这个话题上不够清晰。我正在尝试创建用于将大 class 插入向量的函数。在这个例子中,我使用整数向量作为大 class.
#include <vector>
#include <iostream>
using namespace std;
vector<vector<int>> vectorOfVectors;
void fn(const vector<int> &v) {
vectorOfVectors.push_back(v);
}
void fn(vector<int> &&v) {
vectorOfVectors.push_back(std::move(v));
}
int main() {
vector<int> a({1});
const vector<int> b({2});
fn(std::move(a));
fn(b);
cout<<b[0];
}
显然,我希望尽可能不进行复制。我的问题:
- 这段代码做对了吗?
- 有更好的方法吗?
- 对于使用自定义 classes 的相同方法,我是否需要定义移动构造函数?
Does this code do the right thing?
是的。 C++11 正是出于这个原因添加了 std::vector::push_back(T&&)
。
Is there a better way of doing this?
你的 fn(const vector<int> &v)
和 fn(vector<int> &&v)
都在做同样的事情,将参数 v
推到 vectorOfVectors
的末尾。您可以使用一个使用完美转发的函数模板来代替您的两个 fn
函数。
template<typename T>
void fn(T &&v) {
vectorOfVectors.push_back(std::forward<T>(v));
}
这要归功于 C++11 reference collapsing rules 和 std::forward
。在 v
是左值的情况下,模板类型 T
变为 vector<int>&
,而在 v
是右值的情况下,模板类型 vector<int>&&
变为 vector<int>&
。引用折叠规则意味着 vector<int>& &&
变为 vector<int>&
而 vector<int>&& &&
变为 vector<int>&&
。这正是你想要的,调用 push_back
的版本,在左值的情况下执行复制,而在右值的情况下调用移动的版本。
一个缺点是,当您出错时,这有时会导致有趣的诊断。 ("Interesting" 这里表示来自 g++ 或 clang++ 的数百行高深莫测的诊断文本)。另一个缺点是模板可能导致 "converters gone wild".
的情况For the same approach to work with custom classes, do I need to define move constructors?
不一定。如果 class 没有声明用户定义的析构函数、复制构造函数、复制赋值运算符或移动赋值运算符,您将获得一个隐式声明的移动构造函数。如果 class 具有不可移动的数据成员或派生自无法移动或删除的 class,则隐式声明的移动构造函数将被定义为已删除。
对我来说,这有点太难记了。我不知道这是一个好习惯还是一个坏习惯,但我已经开始使用 Foo(const Foo&)=default
,对五个函数的其他规则使用类似的声明。在许多情况下,我还将构造函数限定为 explicit
以避免 "converters gone wild" 问题。
- 它确实避免了复制
a
。 是的。使用
push_back
意味着您必须至少构造两个对象,而emplace_back
和完美转发可以做更少的工作。template<typename... Ts> auto fn(Ts &&...ts) -> decltype(vectorOfVectors.emplace_back(std::forward<Ts>(ts)...), void()) { vectorOfVectors.emplace_back(std::forward<Ts>(ts)...); }
3。只要您使用 push_back
,您就需要 类 可移动构造以避免复制。如果您可以获得默认定义,则不一定需要自己定义移动构造函数。