请原谅我对这个话题不够清晰。我正在尝试创建用于将大类插入向量的函数。在这个例子中,我使用int的向量作为大类。
#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];
}
显然,我希望在可能的情况下不要复制。我的问题:
答案 0 :(得分:7)
这段代码做得对吗?
是。由于这个原因,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
。在T
是左值的情况下,模板类型vector<int>&
变为v
,而在vector<int>&&
是右值的情况下,v
变为vector<int>& &&
。参考折叠规则意味着vector<int>&
变为vector<int>&& &&
而vector<int>&&
变为push_back
。这完全符合您的要求,调用Foo(const Foo&)=default
的版本,在左值的情况下执行副本,但在右值的情况下执行移动的版本。
一个缺点是,当您出错时,这有时会导致有趣的诊断。 (“有趣”在这里意味着来自g ++或clang ++的数百行不可思议的诊断文本)。另一个缺点是模板可能导致“转换器疯狂”的情况。
对于使用自定义类的相同方法,我是否需要定义移动构造函数?
不一定。如果类未声明用户定义的析构函数,复制构造函数,复制赋值运算符或移动赋值运算符,则将获得隐式声明的移动构造函数。如果类具有不可移动的数据成员或派生自无法移动或删除的类,则隐式声明的移动构造函数将被定义为已删除。
对我而言,这有点太难记了。我不知道这是一个好的做法还是坏的做法,但我已经开始使用explicit
,对五个函数的其他规则使用类似的声明。在许多情况下,我还会将构造函数限定为U
,以避免“转换器疯狂”问题。
答案 1 :(得分:2)
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
,就需要将类移动为可构造的,以避免复制。如果你能得到默认的定义,你自己不一定需要自己定义移动构造器。