【问题标题】:Create vector of tuples from two vectors by move通过移动从两个向量创建元组向量
【发布时间】:2019-11-20 11:20:37
【问题描述】:

我想通过移动std::vectors 的数据从两个std::vector 中创建std::vectorstd::tuple (std::vector<std::tuple<Ts...>>)。

假设我有一个与此类似的结构(添加 std::couts 以展示问题)。

template<typename T>
struct MyType
{
    constexpr MyType() { std::cout << "default constructor\n"; }
    constexpr MyType(const T& data) : m_data(data)
    {
        std::cout << "data constructor\n";
    }
    constexpr MyType(const MyType& other) : m_data(other.m_data)
    {
        std::cout << "copy constructor\n"; 
    }

    constexpr MyType(MyType&& other) noexcept : m_data(std::move(other.m_data))
    {
        std::cout << "move constructor\n";
    }
    ~MyType() = default;

    constexpr MyType& operator=(const MyType& other)
    {
        std::cout << "copy operator\n";
        m_data = other.m_data;
        return *this;
    }
    constexpr MyType& operator=(MyType&& other) noexcept
    {
        std::cout << "move operator\n";
        m_data = std::move(other.m_data);
        return *this;
    }

private:
    T m_data{};
};

现在我们可以为std::vector&lt;MyType&lt;T&gt;&gt;的右值引用定义一个operator+

template<typename LhsT, typename RhsT>
constexpr auto operator+(std::vector<MyType<LhsT>>&& lhs, std::vector<MyType<RhsT>>&& rhs)
{
    if(lhs.size() != rhs.size())
        throw std::runtime_error("");

    std::vector<std::tuple<MyType<LhsT>, MyType<RhsT>>> ret(lhs.size());

    std::cout << "before transform\n";
    std::transform(std::make_move_iterator(lhs.cbegin()),
                   std::make_move_iterator(lhs.cend()),
                   std::make_move_iterator(rhs.cbegin()),
                   ret.begin(),
                   [](auto&& lhs_val, auto&& rhs_val) {
        return std::make_tuple(lhs_val, rhs_val);
    });
    std::cout << "after transform\n";
    return ret;
}

现在我遇到了问题。运行此代码时

int main()
{
    std::vector<MyType<int>> int_vec(1);
    std::vector<MyType<float>> float_vec(1);

    std::cout << "before move operator+\n";
    auto int_float_tp_vec = std::move(int_vec) + std::move(float_vec);
    std::cout << "after move operator+\n";
}

输出是这样的:

default constructor
default constructor
before move operator+
default constructor
default constructor
before transform
copy constructor
copy constructor
move operator
move operator
after transform
after move operator+

问题是:为什么会调用copy constructor?在这里使用std::make_tuple 是错误的方法吗?如果不调用copy constructorcopy operator,我怎么能做到这一点?

LIVE DEMO

【问题讨论】:

  • 从 const 移动一般是一个副本。

标签: c++ vector tuples c++17 move-semantics


【解决方案1】:

你忘了在这里使用std::move

[](auto&& lhs_val, auto&& rhs_val) {
    return std::make_tuple(std::move(lhs_val), std::move(rhs_val));
});

记住,所有命名都是左值。

此外,您必须修复迭代器:

std::transform(lhs.begin(),
               lhs.end(),
               rhs.begin(),
               ret.begin(),
               [](auto&& lhs_val, auto&& rhs_val) {
    return std::make_tuple(std::move(lhs_val), std::move(rhs_val));
});

您不能从 const 迭代器中创建移动迭代器,这类似于将 std::move 应用于 const 类型。事实上,我们可以不使用移动迭代器,只需使用左值引用调用我们的 lambda,然后我们就可以从中移动。

【讨论】:

    【解决方案2】:

    您使用 const 迭代器,但您没有 std::move将元素放入元组中。两者都强制复制。改成这样:

    std::transform(std::make_move_iterator(lhs.begin()),
                   std::make_move_iterator(lhs.end()),
                   std::make_move_iterator(rhs.begin()),
                   ret.begin(),
                   [](auto&& lhs_val, auto&& rhs_val) {
        return std::make_tuple(std::move(lhs_val), std::move(rhs_val));
    });
    

    【讨论】:

      【解决方案3】:

      这个问题已经得到解答,但我想指出,如果您放弃打印输出,转而依赖编译器诊断,那么您在解决问题方面的运气会好得多。您也可以稍微简化一下代码。所以你最终得到的是:

      template<typename T>
      struct MyType
      {
          constexpr MyType();
          constexpr MyType(const T& ) = delete;
          constexpr MyType(const MyType& ) = delete;
      
          constexpr MyType(MyType&& ) noexcept;
          ~MyType() = default;
      
      };
      
      template<typename LhsT, typename RhsT>
      constexpr auto operator+(std::vector<MyType<LhsT>>&& lhs, std::vector<MyType<RhsT>>&& rhs)
      {
      
          std::vector<std::tuple<MyType<LhsT>, MyType<RhsT>>> ret(lhs.size());
      
          std::transform(std::make_move_iterator(lhs.cbegin()),
                         std::make_move_iterator(lhs.cend()),
                         std::make_move_iterator(rhs.cbegin()),
                         ret.begin(),
                         [](MyType<int>&& lhs_val, MyType<float>&& rhs_val) {   
              return std::make_tuple(std::move(lhs_val), std::move(rhs_val));
          });
          return ret;
      }
      
      int main()
      {
          std::vector<MyType<int>> int_vec(1);
          std::vector<MyType<float>> float_vec(1);
      
          auto int_float_tp_vec = std::move(int_vec) + std::move(float_vec);
      }
      

      还有一个非常明确的信息:

      错误:将类型“MyType&&”的引用绑定到“const” MyType' 丢弃限定符

      这立即指出了 const 迭代器的错误使用。

      【讨论】:

      • 正是在这些金块中,我找到了来到这里的理由。这是一种非常简单的使用语言的方法。我本身就是一个“调试打印机”——但它永远不会打败这个。
      猜你喜欢
      • 2016-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-14
      • 2011-06-24
      • 2017-09-09
      • 1970-01-01
      相关资源
      最近更新 更多