Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'Reflection' using structured-bindings

Tags:

c++

c++20

I was wondering if it was possible to use fact that in

struct Foo
{
    int x;
};

int main()
{
    Foo foo;
    auto [a0] = foo; 
    auto [a1, b1] = foo;
    auto [a2, b2, c2] = foo;
    // auto [a, b, c, ...] = foo;
}

only the first structured-binding is valid, in order to create metafunction that takes any pod type and returns a tuple consisting of all the member types of the passed pod, e.g. for Foo it would return

std::tuple<int>

I tried something like this, but that didn't work out:

#include <tuple>
#include <type_traits>
#include <utility>

using namespace std;

template<typename T, typename U = void>
struct to_tuple;

template<typename T>
struct to_tuple<T, void_t<decltype([](T x) {
        auto [a] = x;
        return make_tuple(a);
    }(declval<T>()))>>
{
    using type = decltype([](T x) {
        auto [a] = x;
        return make_tuple(a);
    }(declval<T>()));
};

template<typename T>
struct to_tuple<T, void_t<decltype([](T x) {
        auto [a, b] = x;
        return make_tuple(a, b);
    }(declval<T>()))>>
{
    using type = decltype([](T x) {
        auto [a, b] = x;
        return make_tuple(a, b);
    }(declval<T>()));
};

template<typename T>
using to_tuple_t = typename to_tuple<T>::type;

struct Bar1
{
    int x;
};

struct Bar2
{
    int x;
    float y;
};

int main()
{
    static_assert(is_same_v<to_tuple_t<Bar1>, tuple<int>>);
    static_assert(is_same_v<to_tuple_t<Bar2>, tuple<int, float>>);
}
like image 709
Yamahari Avatar asked Jul 02 '20 08:07

Yamahari


1 Answers

The main issue here is that a structured bindings declaration is just that -- a declaration. We cannot form a "structured binding expression" in order to get the type, which is required in metaprogramming to constrain a template (for e.g., the void_t pattern, or a concept/requires clause)


Per [dcl.struct.bnd], despite the presence of an assignment-expression, a structured binding is distinctly a declaration.

Template metaprogramming relies on types, and a declaration is without a type. You cannot for example say decltype(int a = 10) or even decltype(int a) for this reason.

You can say decltype(int{}) because now we have an expression. For this reason, your void_t pattern does not work.

Unfortunately concepts/constraints don't help us, because we cannot have a declaration inside a requires clause (per [gram.expr])


Personally I think it's a bit of an oversight that we cannot have the equivalent of int{} for structured bindings. But then again, reflection is on its way anyway, so the point is probably moot.

Edit: As dfri pointed out, there is a GNU extension called statement expressions that can make this possible (as an extension it is understandably not portable code)

like image 84
AndyG Avatar answered Nov 05 '22 22:11

AndyG