命名右值引用

时间:2015-11-05 05:33:17

标签: c++ c++11 rvalue-reference

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

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

  1. 这段代码做得对吗?
  2. 有更好的方法吗?
  3. 对于使用自定义类的相同方法,我是否需要定义移动构造函数?

2 个答案:

答案 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 rulesstd::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)

  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. 3。只要您使用push_back,就需要将类移动为可构造的,以避免复制。如果你能得到默认的定义,你自己不一定需要自己定义移动构造器。