2015-11-05 56 views
4

请原谅我对此主题的不清晰。我正在尝试创建将大类插入向量的函数。在这个例子中,我使用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]; 
} 

显然,我希望在可能的情况下不要复制。我的问题:

  1. 此代码是否正确?
  2. 有没有更好的方法来做到这一点?
  3. 对于使用自定义类的相同方法,是否需要定义移动构造函数?

回答

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 rulesstd::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以避免“转换器疯狂”问题。

+0

可否请您详细说明“有些情况下是”部分?以及模板如何工作? – SPMP

+0

@ user2308211 - 我将“在某些情况下,是”更改为您的特定情况。推广一下,如果你有一个'f(T&)'和'f(T &&)'除了一个'std :: move'或两个除了'std :: move'之外都是相同的复制粘贴,你应该考虑对一个函数进行templatizing在移动版本的f()中。 –

+2

“模板类型'T'在'v'是一个右值'的情况下成为'vector &&' - 否,在这种情况下'T'只是'vector '。转发参考版本还捕捉太阳下的所有东西(重要的是如果你有重载或需要检查'f(something)'是否格式良好)。最后,如果所讨论的类型移动起来很便宜(比如'vector '),一个更简单的方法是通过值取参数,然后无条件地移动:void f(vectorOfOfVectors.push_back(std ::移动(v)); }'。 –

2
  1. 它确实避免了复制a
  2. 是的。使用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)...); 
    } 
    

    http://melpon.org/wandbox/permlink/sT65g3sDxHI0ZZhZ

3.只要你使用push_back,你需要的类,以避免拷贝被移动构造的。如果可以获取默认定义,则不一定需要自己定义移动构造函数。