命名右参考
请原谅我对此主题的不清晰。我正在尝试创建将大类插入向量的函数。在这个例子中,我使用ints作为大类。命名右参考
#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];
}
显然,我希望在可能的情况下不要复制。我的问题:
- 此代码是否正确?
- 有没有更好的方法来做到这一点?
- 对于使用自定义类的相同方法,是否需要定义移动构造函数?
此代码是否正确?
是。出于这个原因,C++ 11增加了std::vector::push_back(T&&)
。
有没有更好的方法来做到这一点?
你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>&&
。这正是你想要的,调用版本push_back
在左值的情况下做了副本,但是在右值情况下做了移动的版本。
一个缺点是,这有时会导致有趣的诊断,当你弄错了。 (这里的“有趣的”意味着来自g ++或clang ++的数百行不可思议的诊断文本)。另一个缺点是模板可能导致“转换器疯狂”的情况。
对于使用自定义类的相同方法,是否需要定义移动构造函数?
不一定。如果类未声明用户定义的析构函数,复制构造函数,复制赋值运算符或移动赋值运算符,则会得到隐式声明的移动构造函数。如果类具有不可移动的数据成员或从不能移动或删除的类派生,则隐式声明的移动构造函数将被定义为删除。
对我来说,这有点太难记。我不知道这是一种好的做法还是坏的做法,但我已经开始使用Foo(const Foo&)=default
,对于五个函数的其他规则也有类似的声明。在很多情况下,我还会将构造函数限定为explicit
以避免“转换器疯狂”问题。
- 它确实避免了复制
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
,你需要的类,以避免拷贝被移动构造的。如果可以获取默认定义,则不一定需要自己定义移动构造函数。
可否请您详细说明“有些情况下是”部分?以及模板如何工作? – SPMP
@ user2308211 - 我将“在某些情况下,是”更改为您的特定情况。推广一下,如果你有一个'f(T&)'和'f(T &&)'除了一个'std :: move'或两个除了'std :: move'之外都是相同的复制粘贴,你应该考虑对一个函数进行templatizing在移动版本的f()中。 –
“模板类型'T'在'v'是一个右值'的情况下成为'vector &&' - 否,在这种情况下'T'只是'vector '。转发参考版本还捕捉太阳下的所有东西(重要的是如果你有重载或需要检查'f(something)'是否格式良好)。最后,如果所讨论的类型移动起来很便宜(比如'vector '),一个更简单的方法是通过值取参数,然后无条件地移动:void f(vectorOfOfVectors.push_back(std ::移动(v)); }'。 –