通过移动从两个向量创建元组向量
Create vector of tuples from two vectors by move
我想通过移动 std::vector
的数据从两个 std::vector
创建 std::vector
个 std::tuple
(std::vector<std::tuple<Ts...>>
) s.
假设我有一个与此类似的结构(添加 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{};
};
现在我们可以为 std::vector<MyType<T>>
:
的右值引用定义一个 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 constructor
或 copy operator
的情况下执行此操作?
LIVE DEMO
你忘了在这里使用 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 迭代器创建 move-iterator,这类似于 std::move
应用于 const
类型。事实上,我们可以不使用 move-iterators,只使用左值引用调用我们的 lambda,然后我们可以从中移动。
您使用了 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));
});
问题已经得到解答,但我想指出的是,如果您放弃打印输出,而是依靠编译器诊断,那么您在解决问题方面的运气会好得多。您也可以稍微简化代码。所以你最终得到的是:
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);
}
还有一条非常明确的信息:
error: binding reference of type 'MyType&&' to 'const
MyType' discards qualifiers
这立即指出了对 const 迭代器的错误使用。
我想通过移动 std::vector
的数据从两个 std::vector
创建 std::vector
个 std::tuple
(std::vector<std::tuple<Ts...>>
) s.
假设我有一个与此类似的结构(添加 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{};
};
现在我们可以为 std::vector<MyType<T>>
:
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 constructor
或 copy operator
的情况下执行此操作?
LIVE DEMO
你忘了在这里使用 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 迭代器创建 move-iterator,这类似于 std::move
应用于 const
类型。事实上,我们可以不使用 move-iterators,只使用左值引用调用我们的 lambda,然后我们可以从中移动。
您使用了 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));
});
问题已经得到解答,但我想指出的是,如果您放弃打印输出,而是依靠编译器诊断,那么您在解决问题方面的运气会好得多。您也可以稍微简化代码。所以你最终得到的是:
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);
}
还有一条非常明确的信息:
error: binding reference of type 'MyType&&' to 'const MyType' discards qualifiers
这立即指出了对 const 迭代器的错误使用。