Структурированная привязка и галстук ()

Учитывая эти декларации:

int a[3] {10,20,30};
std::tuple<int,int,int> b {11,22,33};

Я могу использовать объявления структурированной привязки для декодирования a и b:

auto [x1,y1,z1] = a;
auto [x2,y2,z2] = b;

Но если x1, y1 и т. д. уже существуют, что мне делать?

std::tie(x1,y1,z1) = a;  // ERROR
std::tie(x2,y2,z2) = b;  // OK

Это работает для b, но не для a. Есть ли аналогичная простая конструкция, которая работает для a, или мне нужно получать a[0], a[1] и a[2] отдельно?


person oz1cz    schedule 30.03.2018    source источник


Ответы (2)


Неа.

Структурированные привязки имеют определенные языковые правила для обработки массивов и некоторых других типов. tie() является tuple<T&...> и может быть назначен только из другого tuple<U&...>.


В случае с массивом вы можете написать функцию, которая превратит этот массив в кортеж ссылок на него:

template <typename T, size_t N, size_t... Is>
auto as_tuple_impl(T (&arr)[N], std::index_sequence<Is...>) {
    return std::forward_as_tuple(arr[Is]...);
}

template <typename T, size_t N>
auto as_tuple(T (&arr)[N]) {
    return as_tuple_impl(arr, std::make_index_sequence<N>{});
}

std::tie(x1, y1, z1) = as_tuple(a); // ok

В качестве альтернативы, если вы знаете, сколько существует привязок (что вам в любом случае нужно), вы можете использовать структурированные привязки для возврата кортежа. Но вы должны как указать размер, так и выписать случай для каждого:

template <size_t I, typename T>
auto as_tuple(T&& tuple) {
    if constexpr (I == 1) {
        auto&& [a] = std::forward<T>(tuple);
        return std::forward_as_tuple(a);
    } else if constexpr (I == 2) {
        auto&& [a, b] = std::forward<T>(tuple);
        return std::forward_as_tuple(a, b);
    } else if constexpr (I == 3) {
        // etc.
    }
}

std::tie(x1, y1, z1) = as_tuple<3>(a); // ok
person Barry    schedule 30.03.2018
comment
Во втором случае вы не могли бы SFINAE на get удалить явный размер? - person Passer By; 30.03.2018
comment
@PasserBy get не подходит для SFINAE для std::array, он не определен для необработанного массива (верно?) и определенно не будет работать для типов со всеми общедоступными членами. - person Barry; 30.03.2018
comment
Присмотревшись еще немного, с некоторыми сверхсовременными механизмами, это возможно. - person Passer By; 30.03.2018
comment
@PasserBy Умно, но... вы можете использовать структурированные привязки к типам, которые не являются агрегатами :-) - person Barry; 30.03.2018

Просто для удовольствия... для имитации синтаксиса, похожего на

std::tie(x1,y1,z1) = a;

вы можете написать структуру, которая обертывает массив указателей, с operator=() для соответствующих массивов

template <typename T, std::size_t ... Is>
struct ptrArray<T, std::index_sequence<Is...>>
 {
   std::array<T*, sizeof...(Is)> ap;

   auto & operator= (T (&arr)[sizeof...(Is)])
    {
      ((*ap[Is] = arr[Is]), ...);

      return *this;
    }
 };

и make-функция для этой структуры

template <typename T0, typename ... Ts>
ptrArray<T0, std::make_index_sequence<sizeof...(Ts)+1U>>
   makePtrArray (T0 & t0, Ts & ... ts)
 { return { { { &t0, &ts... } } }; }

и

makePtrArray(x1, y1, z1) = a;

работает.

Ниже приведен полный рабочий пример

#include <array>
#include <iostream>
#include <type_traits>

template <typename, typename>
struct ptrArray;

template <typename T, std::size_t ... Is>
struct ptrArray<T, std::index_sequence<Is...>>
 {
   std::array<T*, sizeof...(Is)> ap;

   auto & operator= (T (&arr)[sizeof...(Is)])
    {
      ((*ap[Is] = arr[Is]), ...);

      return *this;
    }
 };

template <typename T0, typename ... Ts>
ptrArray<T0, std::make_index_sequence<sizeof...(Ts)+1U>>
   makePtrArray (T0 & t0, Ts & ... ts)
 { return { { { &t0, &ts... } } }; }

int main ()
 {
   int x1, y1, z1;
   int a[3] {10,20,30};

   makePtrArray(x1, y1, z1) = a;

   std::cout << x1 << ' ' << y1 << ' ' << z1 << std::endl;
 }
person max66    schedule 30.03.2018