Vladimir Shttl
Vladimir Shttl

Reputation: 61

Structured binding declaration for class inherited from tuple

I have a class inherited from standard tuple:

#include <concepts>
#include <cstdint>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>

template< typename ... Args >
class Identifier: public std::tuple< Args... >
{
public:
   Identifier( Args... args ) requires( sizeof...( Args ) != 0 ) : std::tuple< Args... >( std::move( args )... )
   {}

   Identifier( std::tuple< Args... > tuple ):std::tuple< Args... >( tuple )
   {}

   Identifier()
   {}

   Identifier(std::convertible_to<std::tuple<Args...>> auto&& identifier)
           : std::tuple<Args...>(std::forward<decltype(identifier)>(identifier)) {}
};

int main()
{
   Identifier<std::wstring, std::int64_t>  id(L"id", 10);
   auto& [ str_id, num_id ] = id;

   std::tuple< std::wstring, int64_t > tuple_id(L"id", 10);
   auto& [ str_t_id, num_t_id ] = tuple_id;

   return 0;
}

in line

auto& [ str_id, num_id ] = item;

I got error Type 'Identifier<std::wstring, int64_t>' (aka 'Identifier<basic_string<wchar_t>, long long>') decomposes into 1 element, but 2 names were provided

but the same code for tuple variable its ok

auto& [ str_t_id, num_t_id ] = tuple_id;

What I have to do to support structured binding for class Identifier ?

Upvotes: 2

Views: 71

Answers (3)

Vladimir Shttl
Vladimir Shttl

Reputation: 61

Implemented the classes tuple_size and tuple_element and it worked

template < class... Args >
struct std::tuple_size< sbis::IdentifierType< Args...> > : public std::integral_constant< size_t, sizeof...( Args ) > {};

template< std::size_t I, class... Args >
class std::tuple_element< I, sbis::IdentifierType< Args... > > : public tuple_element< I, std::tuple< Args... > >{};

Upvotes: 0

JF4
JF4

Reputation: 486

Structured binding won't work with std::tuple and a template parameter pack

You need to specialize std::tuple and implement tuple_size and tuple_element

Examples here: Direct initialization fails with class derived from std::tuple, while it works for std::pair

Upvotes: 0

Useless
Useless

Reputation: 67782

I can't see a direct duplicate for this, although there are overlapping questions on tuple-like binding protocol.

For this case (not an array, not a simple aggregate: specifically case 2 here),

  • The expression std::tuple_size<E>::value must be a well-formed integral constant expression,

but it isn't. Having std::tuple_size<std::tuple<...>> defined doesn't help a subclass, because implicit conversions don't happen here.

You'll need to specialize it yourself.

  • For each structured binding, a variable whose type is "reference to std::tuple_element<I, E>::type" is introduced:...

You'll need to specialize this too. They can both just forward to the std::tuple versions.

You also need

  • e.get<I>(), if lookup for the identifier get in the scope of E by class member access lookup finds at least one declaration ...
  • Otherwise, get<I>(e), where get is looked up by argument-dependent lookup only, ignoring non-ADL lookup.

But here we finally have ADL doing the work of finding the normal std::get for us.

Upvotes: 0

Related Questions