有没有一种可扩展的方法来从 C++ 中的数组初始化结构?
Is there an extensible way to initialize a struct from an array in C++?
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})
}
如代码示例所示,是否有一些快捷方式可以使用 arr 初始化 someStruct?
而不是诉诸:
SomeStruct someStruct {arr[0], arr[1], arr[2], arr[3]};
您可以使用 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 中,这保证使用复制省略,因此只会构造一个 SomeStruct
。在 C++17 之前,可能会涉及一些移动构造(尽管编译器已经被允许并且确实实现了具有相同效果的 return 值优化)。
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'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>>>{});
}
并将 std::tuple_size_v<...>
替换为 std::tuple_size<...>::value
。
这是 T(...)
版本的。您需要将 T(...)
替换为 T{...}
才能获得我在上面实现的 T{...}
版本。
您可以定义一个带有一对迭代器的构造函数。有时简单胜过灵活性。
#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++;
}
};
您可以使用其中一项内存传输功能。 {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;
}
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})
}
如代码示例所示,是否有一些快捷方式可以使用 arr 初始化 someStruct? 而不是诉诸:
SomeStruct someStruct {arr[0], arr[1], arr[2], arr[3]};
您可以使用 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 中,这保证使用复制省略,因此只会构造一个 SomeStruct
。在 C++17 之前,可能会涉及一些移动构造(尽管编译器已经被允许并且确实实现了具有相同效果的 return 值优化)。
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'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>>>{}); }
并将 std::tuple_size_v<...>
替换为 std::tuple_size<...>::value
。
这是 T(...)
版本的。您需要将 T(...)
替换为 T{...}
才能获得我在上面实现的 T{...}
版本。
您可以定义一个带有一对迭代器的构造函数。有时简单胜过灵活性。
#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++;
}
};
您可以使用其中一项内存传输功能。 {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;
}