Добавление элементарных кортежей

У меня есть некоторые значения, хранящиеся в кортеже, и я хочу добавить еще один кортеж к нему по элементам. Поэтому я хотел бы иметь такую функциональность:

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

Если выходной кортеж имел бы значение {3,6}

Смотрел ссылку на CPP, но я не мог найти эту функциональность. Возможно, что этот и этот вопрос актуальны, однако ответы затуманиваются другими сложностями.

Ответ 1

Вы также можете рассмотреть использование std :: valarray, поскольку он позволяет точно то, что вам кажется нужным.

#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;
}

Ответ 2

Вы можете использовать что-то вроде этого, которое поддерживает все три ваших предложения синтаксиса:

#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;
}

Рабочий пример:

http://coliru.stacked-crooked.com/a/27b8cf370d44d3d5

http://coliru.stacked-crooked.com/a/ff24dae1c336b937


В большинстве случаев я бы по-прежнему работал с именованными структурами. Кортежи редко являются правильным выбором.

Ответ 3

Решение @lubgr является удовлетворительным для вашего конкретного случая использования. Я предлагаю вам общее решение, которое будет работать для кортежей разных типов, а также кортежей разных (равных) размеров.

#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)>{});
}

Я использовал несколько возможностей С++ 17 (главным образом, для шаблонов), без которых код стал бы немного сложнее. Возможным улучшением будет использование семантики перемещения.

Очевидно, это относится к первому "возможному синтаксису", который вы предоставили.

Ответ 4

Ниже приведено определение оператора для синтаксиса # 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));
}

СинтаксиС# 2 и # 3 невозможен без создания настраиваемой структуры, поскольку они могут быть определены только как члены классов, на которых они работают (и вы не можете трогать существующие классы в пространстве имен std).