Reputation: 57
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})
}
As seen in the code example, is there some shortcut to initialize someStruct with arr? Instead of resorting to:
SomeStruct someStruct {arr[0], arr[1], arr[2], arr[3]};
Upvotes: 3
Views: 635
Reputation: 22152
You can use std::apply
to create a parameter pack and use that in the initializer:
auto someStruct = std::apply([](auto&&... args){return SomeStruct{decltype(args)(args)...};}, arr);
You can put this in a function to use it comfortably:
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));
}
Then use it as
auto someStruct = make_from_tuple_list_init<SomeStruct>(arr);
This requires C++17 and #include<tuple>
, but can be implemented in earlier C++ versions (with some more effort) as well. Also in C++17 this is guaranteed to use copy elision, so there will be only one SomeStruct
constructed. Before C++17 some move constructions might be involved (although compilers were already permitted to and did implement return value optimization with the same effect).
std::apply
also has the benefit that it works with every type that implements std::get
and std::tuple_size
, meaning that it will work for example with std::array
, std::tuple
, std::pair
and other user-defined types with that interface.
One might want to make two versions of this function, one that uses T{...}
as above and one that uses T(...)
, so that one is able to choose between list-initialization and direct-initialization.
However, the T(...)
version has been implemented in the standard library since C++17 as std::make_from_tuple
. In C++20, aggregate initialization will also work with parentheses, so std::make_from_tuple
will work for your case then directly.
For a C++14 implementation, take this example from cppreference's page on 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>>>{}); }
and replace std::tuple_size_v<...>
with std::tuple_size<...>::value
.
This is for the T(...)
version. You would need to replace T(...)
with T{...}
to get the T{...}
version I implemented above.
Upvotes: 11
Reputation: 1
You can use one of the Memory Transfer functions. {memcpy, CopyMemory}
this is an example:
#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;
}
Upvotes: -2
Reputation: 40080
You can define a constructor taking a pair of iterators. Sometimes simplicity wins over flexibility.
#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++;
}
};
Demo: https://coliru.stacked-crooked.com/a/7e235a8022b048ac
Upvotes: 1