通过移动从两个向量创建元组向量

时间:2019-07-10 19:20:58

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

我想通过移动std::vector的数据,从两个std::tuple的{​​{1}}(std::vector<std::tuple<Ts...>>)中创建一个std::vector

假设我具有与此类似的结构(添加了std::vector来展示问题)。

std::cout

现在我们可以为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{}; }; 的右值引用定义operator+

std::vector<MyType<T>>

现在我正在面对这个问题。运行此代码时

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_tuplecopy constructor呢?

LIVE DEMO

3 个答案:

答案 0 :(得分:5)

您忘了在这里使用export ASAN_OPTIONS=halt_on_error=false

std::move

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

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

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

您不能使用const迭代器制作move-iterator,这类似于应用于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)); }); 类型的std::move。实际上,我们可以不使用move-iterators,而只需使用左值引用调用我们的lambda,然后我们就可以从中进行移动。

答案 1 :(得分:5)

您使用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));
});

答案 2 :(得分: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'丢弃限定符

这立即表明错误使用了常量迭代器。