I have some values held in a tuple, and I am looking to add another tuple to it element-wise. So I would like functionality like this:
std::tuple<int,int> a = {1,2};
std::tuple<int,int> b = {2,4};
std::tuple<int,int> c = a + b; // possible syntax 1
a += b; // possible syntax 2
a += {2,4}; // possible syntax 3
Where the output tuple would have the value {3,6}
Was looking at CPP reference, but I couldn't find this functionality. It's possible that this and this question are relevant, however the answers are obfuscated by other complexities.
You could also consider using std::valarray since it allows exactly the things that you seem to want.
#include <valarray>
int main()
{
std::valarray<int> a{ 1, 2 }, b{ 2, 4 }, c;
c = a - b; // c is {-1,-2}
a += b; // a is {3,6}
a -= b; // a is {1,2} again
a += {2, 4}; // a is {3,6} again
return 0;
}
You could use something like this, which supports all three of your syntax proposals:
#include <tuple>
#include <utility>
namespace internal
{
//see: https://stackoverflow.com/a/16387374/4181011
template<typename T, size_t... Is>
void add_rhs_to_lhs(T& t1, const T& t2, std::integer_sequence<size_t, Is...>)
{
auto l = { (std::get<Is>(t1) += std::get<Is>(t2), 0)... };
(void)l; // prevent unused warning
}
}
template <typename...T>
std::tuple<T...>& operator += (std::tuple<T...>& lhs, const std::tuple<T...>& rhs)
{
internal::add_rhs_to_lhs(lhs, rhs, std::index_sequence_for<T...>{});
return lhs;
}
template <typename...T>
std::tuple<T...> operator + (std::tuple<T...> lhs, const std::tuple<T...>& rhs)
{
return lhs += rhs;
}
Working example:
http://coliru.stacked-crooked.com/a/27b8cf370d44d3d5
http://coliru.stacked-crooked.com/a/ff24dae1c336b937
I would still go with named structs in most cases. Tuples are seldom the correct choice.
The solution by @lubgr is satisfactory for your specific use-case. I offer you a generic solution which will work for tuples of different types, as well tuples of different (equal) sizes.
#include <tuple>
#include <utility>
#include <iostream>
template<typename... T1, typename... T2, std::size_t... I>
constexpr auto add(const std::tuple<T1...>& t1, const std::tuple<T2...>& t2,
std::index_sequence<I...>)
{
return std::tuple{ std::get<I>(t1) + std::get<I>(t2)... };
}
template<typename... T1, typename... T2>
constexpr auto operator+(const std::tuple<T1...>& t1, const std::tuple<T2...>& t2)
{
// make sure both tuples have the same size
static_assert(sizeof...(T1) == sizeof...(T2));
return add(t1, t2, std::make_index_sequence<sizeof...(T1)>{});
}
I used a few C++17 features (mainly template-related) without which the code would become a bit more complicated. A possible improvement would be to make use of move-semantics.
Obviously, this applies for the first "possible syntax" you provided.
Here is an operator definition for syntax #1:
template <class S, class T> std::tuple<S, T> operator + (const std::tuple<S, T>& lhs, const std::tuple<S, T>& rhs)
{
return std::make_tuple(std::get<0>(lhs) + std::get<0>(rhs), std::get<1>(lhs) + std::get<1>(rhs));
}
Syntax #2 and #3 are not possible without creating a custom struct, because they can only be defined as members of the classes they operate on (and you can't touch existing classes in namespace std
).
来源:https://stackoverflow.com/questions/50814669/element-wise-tuple-addition