考虑以下代码:
#include <tuple>
#include <type_traits>
struct foo
{
struct a {};
struct b {};
struct c {};
};
// we use here an hypothetical type trait 'get_struct_list_t'
using T = get_struct_list_t<foo>;
static_assert (std::is_same_v <T, std::tuple<foo::a, foo::b, foo::c>>);
int main () {}
问题:是否可以设计一个类型特征
get_struct_list_t
来返回给定结构的内部结构类型列表?
我认为在C++中这是不可能的,因为它需要具有一些反射功能,所以我的问题主要是为了确认这一点。
最初的问题没有对必须检索其类型列表的内部结构做出任何假设。
如果这些内部结构只保存类型信息(即没有数据成员),我们可以使用以下技巧:我们可以定义一个匿名结构的成员,而不是命名实例
struct a {};
,即。 struct {} a;
。现在的好处是我们可以从中获取类型信息,换句话说,我们可以使用一些struct_to_tuple
机制来获取我们想要的东西。然而,对于最终用户来说,这个解决方案非常笨拙且具有误导性。这比日常使用更有趣......
这是整个示例。请注意,我在这里使用以下 gist 从结构中获取 std::tuple
。
#include <type_traits>
#include <tuple>
///////////////////////////////////////////////////////////////////////////////
// https://gist.github.com/utilForever/1a058050b8af3ef46b58bcfa01d5375d
///////////////////////////////////////////////////////////////////////////////
template<class T, typename... Args> decltype(void(T{std::declval<Args>()...}), std::true_type()) test (int);
template<class T, typename... Args> std::false_type test (...);
template<class T, typename... Args> struct is_braces_constructible : decltype(test<T, Args...>(0)) {};
struct any_type { template<class T> constexpr operator T(); };
template<class T>
auto to_tuple(T&& object) noexcept
{
using type = std::decay_t<T>;
if constexpr(is_braces_constructible<type, any_type, any_type, any_type, any_type>{}) {
auto&& [p1, p2, p3, p4] = object;
return std::make_tuple(p1, p2, p3, p4);
} else if constexpr(is_braces_constructible<type, any_type, any_type, any_type>{}) {
auto&& [p1, p2, p3] = object;
return std::make_tuple(p1, p2, p3);
} else if constexpr(is_braces_constructible<type, any_type, any_type>{}) {
auto&& [p1, p2] = object;
return std::make_tuple(p1, p2);
} else if constexpr(is_braces_constructible<type, any_type>{}) {
auto&& [p1] = object;
return std::make_tuple(p1);
} else {
return std::make_tuple();
}
}
///////////////////////////////////////////////////////////////////////////////
// The definition of our trait
///////////////////////////////////////////////////////////////////////////////
template<typename T>
struct get_struct_list
{
using type = decltype (to_tuple (T{}));
};
template<typename T>
using get_struct_list_t = typename get_struct_list<T>::type;
////////////////////////////////////////////////////////////
// A testing struct
////////////////////////////////////////////////////////////
struct foo
{
// the 'inversion' trick is here
// -> use members of anonymous struct instead of named structs
struct { using type=int; } a; // instead of struct a { using type=int; };
struct { using type=char; } b;
struct { using type=float; } c;
};
using T = get_struct_list_t<foo>;
static_assert (std::is_same_v <std::tuple_element_t<0,T>::type, int >);
static_assert (std::is_same_v <std::tuple_element_t<1,T>::type, char >);
static_assert (std::is_same_v <std::tuple_element_t<2,T>::type, float>);
int main () {}