Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get non-member get<N> to work for custom class in a namespace [C++17]

C++17 introduced structured binding declarations: auto [a, b] = some_tuple;.

This works out of the box for things like std::tuple. It is also possible to make it work for custom types, you just have to provide (among other things) an get-function template, either as member or outside the custom class.

For the standard classes, this is done via a non-member get lying in the std-namespace: auto a = std::get<0>(some_tuple); works, but not auto a = some_tuple.get<0>();.

But here it gets weird for me: Since we have to explicitly specify the template parameter N for get, ADL does not work, for example, we can't just write auto a = get<0>(some_tuple);. But then the structured binding declaration with tuples shouldn't work too, because it's just syntactic sugar for calls like either get<N>(some_tuple) or some_tuple.get<N>() (modulo some &)! And indeed, when I provide only a non-member version of get for my custom class inside a namespace, it doesn't work! EDIT: Structured binding for custom classes also works fine, see the code snippet in the accepted answer for a minimal example!

So how do the implementers of the standard make structured binding work for e.g. tuples without a get as member, and how can I achieve the same behavior for my custom classes?

like image 565
x432ph Avatar asked Mar 23 '19 16:03

x432ph


1 Answers

They cheat.

But you can emulate their cheating by adding a template get to the global namespace.

template<class T, std::enable_if_t<std::is_same<T,void>{}, bool>>
void get(int)=delete;

which should activate "parse get as a template".

You don't need to do this to get structured bindings working. As noted, the compiler just cheats:

namespace example {
    struct silly {
        int x;
    };
    template<std::size_t I>
    int& get( silly& s ) { return s.x; }
}
namespace std {
    template<>
    struct tuple_size<::example::silly>:std::integral_constant<std::size_t, 1>{};
    template<>
    struct tuple_element<0, ::example::silly>{ using type=int; };
}

int main() {
    example::silly s { 42 };

    auto&& [x] = s;
    std::cout << x;
}
like image 179
Yakk - Adam Nevraumont Avatar answered Oct 06 '22 00:10

Yakk - Adam Nevraumont