Есть ли расширяемый способ инициализации структуры из массива в C ++? - PullRequest
2 голосов
/ 18 февраля 2020
struct SomeStruct {
    int a;
    int b;
    int c;
    int d;
};

void main() {
    std::array<int, 4> arr {1, 2, 3, 4};
    SomeStruct someStruct { ??? }; // <------ Initialize with arr? (Ideal would be {arr})
}

Как видно из примера кода, есть ли ярлык для инициализации someStruct с помощью arr? Вместо того чтобы прибегать к:

    SomeStruct someStruct {arr[0], arr[1], arr[2], arr[3]};

Ответы [ 3 ]

9 голосов
/ 18 февраля 2020

Вы можете использовать std::apply для создания пакета параметров и использовать его в инициализаторе:

auto someStruct = std::apply([](auto&&... args){return SomeStruct{decltype(args)(args)...};}, arr);

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

template<typename T, typename A>
constexpr T make_from_tuple_list_init(A&& a) {
    return std::apply([](auto&&... args){return T{decltype(args)(args)...};}, std::forward<A>(a));
}

Затем используйте его как

auto someStruct = make_from_tuple_list_init<SomeStruct>(arr);

Это требует C ++ 17 и #include<tuple>, но может быть реализовано и в более ранних версиях C ++ (с некоторыми дополнительными усилиями). Также в C ++ 17 это гарантированно использует elision copy, поэтому будет создан только один SomeStruct. До C ++ 17 могли быть задействованы некоторые конструкции перемещения (хотя компиляторы уже разрешили и реализовали оптимизацию возвращаемого значения с тем же эффектом).

std::apply также имеет то преимущество, что работает с каждым типом, который реализует std::get и std::tuple_size, что означает, что он будет работать, например, с std::array, std::tuple, std::pair и другими пользовательскими типами с этим интерфейсом.

Можно сделать два версии этой функции, одна из которых использует T{...}, как указано выше, а другая - T(...), так что вы можете выбирать между инициализацией списка и прямой инициализацией.

Однако версия T(...) был реализован в стандартной библиотеке с C ++ 17 как std::make_from_tuple. В C ++ 20 агрегатная инициализация также будет работать с круглыми скобками, поэтому std::make_from_tuple будет работать для вашего случая, тогда напрямую.


Для реализации C ++ 14, возьмите этот пример из Страница cppreference на std::make_from_tuple:

namespace detail {
template <class T, class Tuple, std::size_t... I>
constexpr T make_from_tuple_impl( Tuple&& t, std::index_sequence<I...> )
{
  return T(std::get<I>(std::forward<Tuple>(t))...);
}
} // namespace detail

template <class T, class Tuple>
constexpr T make_from_tuple( Tuple&& t )
{
   return detail::make_from_tuple_impl<T>(std::forward<Tuple>(t),
       std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}

и замените std::tuple_size_v<...> на std::tuple_size<...>::value.

Это для T(...) версии. Вам нужно заменить T(...) на T{...}, чтобы получить версию T{...}, которую я реализовал выше.

1 голос
/ 18 февраля 2020

Вы можете определить конструктор, используя пару итераторов. Иногда простота побеждает гибкость.

#include <array>

struct SomeStruct {
    int a;
    int b;
    int c;
    int d;

    template<class InputIt>
    SomeStruct(InputIt begin, InputIt end)
    {
        if (begin != end) a = *begin++;
        if (begin != end) b = *begin++;
        if (begin != end) c = *begin++;
        if (begin != end) d = *begin++;
    }
};

Демо: https://coliru.stacked-crooked.com/a/7e235a8022b048ac

0 голосов
/ 18 февраля 2020

Вы можете использовать одну из функций передачи памяти. {memcpy, CopyMemory}

это пример:

#include <iostream>
#include <array>
#include <string.h>

using namespace std;

int main()
{
   array<int,4> ar = {1,2,3,4};

   struct myStruct
   {
      int a;
      int b;
      int c;
      int d;
   };

 myStruct stc;

memcpy(&stc, ar.data(), sizeof(stc));


cout << "My struct elements are: " << stc.a << " - " << stc.b << " - "<< stc.c << " - " << stc.d << endl;
return 0; 
}
...