sadly, it's clearly that there is no way to use a parameter pack in structured binding. and in the other hand, we can't bind the data members without structured binding.
but, if you give the amount of data members, you can bind it by a traditional way:
template<size_t>
struct structToTupleHelper;
template<>
struct structToTupleHelper<0>; // ISO C++17 does not allow a decomposition group to be empty.
template<>
struct structToTupleHelper<1>{
template<typename X, size_t... Is>
static auto convert(X&& x, std::index_sequence<Is...>){
auto&& [a1] = std::forward<X>(x); // bound variables are always thought as lvalue.
auto temp = std::forward_as_tuple(a1);
return std::forward_as_tuple(std::get<Is>(temp)...);
}
};
template<>
struct structToTupleHelper<2>{
template<typename X, size_t... Is>
static auto convert(X&& x, std::index_sequence<Is...>){
auto&& [a1, a2] = std::forward<X>(x); // bound variables are always thought as lvalue.
auto temp = std::forward_as_tuple(a1, a2);
return std::forward_as_tuple(std::get<Is>(temp)...);
}
};
// ...
// maybe 16 is enough?
template<typename X, size_t... Is>
auto structToTuple(X&& x, std::index_sequence<Is...> _1){
return structToTupleHelper<sizeof...(Is)>::convert(std::forward<X>(x), _1);
}
template<size_t N, typename X, size_t... Is>
auto structToTuple(X&& x, std::index_sequence<Is...> _1){
return structToTupleHelper<N>::convert(std::forward<X>(x), _1);
}
template<size_t N, typename X>
auto structToTuple(X&& x){
return structToTupleHelper<N>::convert(std::forward<X>(x), std::make_index_sequence<N>());
}
and then you can use it such as:
int ii;
struct A{
int& a;
} a{ii};
struct B{
int a;
} b;
auto t1 = structToTuple<1>(A{ii}); // tuple<int&>, valid.
auto t2 = structToTuple<1>(a); // tuple<int&>, valid.
auto t3 = structToTuple<1>(B{}); // tuple<int&>, invalid: a lvalue reference is bound to a temporary object.
auto t4 = structToTuple<1>(b); // tuple<int&>, valid.
somefunc(structToTuple<1>(B{})); // valid. the temporary object is alive inside 'somefunc'.
structToTuple<1>(A{ii}) = std::tuple(1); // valid. assign 1 to 'ii'.
structToTuple<1>(a) = std::tuple(1); // valid. assign 1 to 'ii'.
structToTuple<1>(B{}) = std::tuple(1); // unexpected. assign 1 to the member of a temporary object.
structToTuple<1>(b) = std::tuple(1); // valid. assign 1 to 'b.a'.
// struct C{
// int a : 8;
// } c;
// auto t5 = structToTuple<1>(C{}); // invalid: bitfields can not be treated as non-const lvalue reference
// auto t6 = structToTuple<1>(c); // invalid: bitfields can not be treated as non-const lvalue reference
maybe you think it's troublesome to do specializations. fortunately, we can use macro to simplify it: (it's exactly what BOOST always does.)
#define XXX_CONCAT_HELPER(a, b) a##b
#define XXX_CONCAT(a, b) XXX_CONCAT_HELPER(a, b)
#define XXX_COMMA ,
#define XXX_COMMA_FUNC(a) ,
#define XXX_EMPTY
#define XXX_EMPTY_FUNC(a)
#define XXX_REPEAT_0(func, join)
#define XXX_REPEAT_1(func, join) func(1)
#define XXX_REPEAT_2(func, join) XXX_REPEAT_1(func,join) join(2) func(2)
// ...
#define XXX_REPEAT_256(func, join) XXX_REPEAT_255(func,join) join(256) func(256)
#define XXX_REPEAT(func, times, join) XXX_CONCAT(XXX_REPEAT_,times)(func,join)
// macro is not allowed to be recursive, so we need another repeat function.
#define XXX_ALIAS_REPEAT_0(func, join)
#define XXX_ALIAS_REPEAT_1(func, join) func(1)
#define XXX_ALIAS_REPEAT_2(func, join) XXX_ALIAS_REPEAT_1(func,join) join(2) func(2)
// ...
#define XXX_ALIAS_REPEAT_256(func, join) XXX_ALIAS_REPEAT_255(func,join) join(256) func(256)
#define XXX_ALIAS_REPEAT(func, times, join) XXX_CONCAT(XXX_ALIAS_REPEAT_,times)(func,join)
#define STRUCT_TO_TUPLE_TOKEN_FUNC(n) XXX_CONCAT(a,n)
#define STRUCT_TO_TUPLE_FUNC(n) \
template<> \
struct structToTupleHelper<n>{ \
template<typename X, size_t... Is> \
static auto convert(X&& x, std::index_sequence<Is...>){ \
auto&& [XXX_REPEAT(STRUCT_TO_TUPLE_TOKEN_FUNC,n,XXX_COMMA_FUNC)] = std::forward<X>(x); \
auto temp = std::forward_as_tuple(XXX_REPEAT(STRUCT_TO_TUPLE_TOKEN_FUNC,n,XXX_COMMA_FUNC)); \
return std::forward_as_tuple(std::get<Is>(temp)...); \
} \
}; \
XXX_ALIAS_REPEAT(STRUCT_TO_TUPLE_FUNC,128,XXX_EMPTY_FUNC)
#undef STRUCT_TO_TUPLE_TOKEN_FUNC
#undef STRUCT_TO_TUPLE_FUNC