Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Structure Binding : binding to public data members (inherited base class )

Tags:

c++

c++17

Even though both bases class and derived class has public data members

#include <iostream>
class M {
    public:
    int x = 2;
    volatile double y=3;
};

class S:public M {
    public:
    int x1 = 4 ;
    volatile double y1 = 5;

};

int main() {
    S f();
    S a;
    std::cout<<a.x<<a.y<<a.x1<<a.y1;
    auto [b,c,d,e] = f(); 
}

Getting error for

 auto [b,c,d,e] = f(); 

main.cpp: In function 'int main()': main.cpp:21:10: error: cannot decompose class type 'S': both it and its base class 'M' have non-static data members auto [b,c,d,e] = f();

As per http://en.cppreference.com/w/cpp/language/structured_binding

Case 3: binding to public data members

Every non-static data member of E must be a public direct member of E or the same unambiguous public base of E,

Not able to understand what does this mean same unambiguous public base of E,

DEMO

like image 418
Hariom Singh Avatar asked Sep 01 '17 15:09

Hariom Singh


People also ask

What is structural binding?

A structural adhesive can be simply described a 'high-strength glue' that bonds together components in a load-bearing structure. Structural adhesives are used extensively in aircraft for bonding metal-to-metal, metal-to-composite and composite-to-composite parts.

What is structure binding in C++?

Binds the specified names to subobjects or elements of the initializer. Like a reference, a structured binding is an alias to an existing object. Unlike a reference, a structured binding does not have to be of a reference type.

What can be inherited by a derived class from a base?

The derived class inherits all members and member functions of a base class. The derived class can have more functionality with respect to the Base class and can easily access the Base class. A Derived class is also called a child class or subclass.

Which is not inherited from the base class?

Constructor cannot be inherited but a derived class can call the constructor of the base class.


2 Answers

There are three adjectives here, which specify three orthogonal requirements:

  1. same
  2. unambiguous
  3. public

In order, it might help to look at counter-examples. In all cases, assume that template <class T> T make(); exists.


Counter-example for "same": there are two members of D, but they are not members of the same base of D - i is a member of B but j is a member of D:

struct B { int i; };
struct D : B { int j; };

auto [i, j] = make<D>(); // error

To fix this, either j needs to be a direct member of B or i needs to be direct member of D:

struct B { int i, j; };
struct D : B { };
auto [i, j] = make<D>(); // ok

struct B { };
struct D : B { int i, j; };
auto [i, j] = make<D>(); // ok

Counter-example for "unambiguous": there are two members of D, they are both members of B, but it's an ambiguous base class of D.

struct B { int i; };
struct M1 : B { };
struct M2 : B { };
struct D : M1, M2 { };

auto [i, j] = make<D>(); // error

If B were a virtual base of both M1 and M2, then this would be ok:

struct B { int i; };
struct M1 : virtual B { };
struct M2 : virtual B { };
struct D : M1, M2 { };

auto [i] = make<D>(); // ok

Counter-example for "public". This is the simplest one. If the members are in a private base, they aren't accessible anyway:

struct B { int i; };
struct D : private B { };

make<D>().i;          // error, as-is
auto [i] = make<D>(); // error, non-public base, but really same reason

Note also that, as TC points out, the requirement is that the base be public, not that the members be accessible. That is, making the members accessible from the private base will still not work:

struct B { int i; };
struct D : private B { using B::i; };

make<D>().i;        // ok now, due to the using-declaration
auto [i] = make<D>(); // still error, B is still private base

Of course, in all of these counterexample cases, just because all the members are not in the same, unambiguous, public base class of E doesn't mean it's unusuable with structured bindings. It just means that you have to write out the bindings yourself:

struct B { int i; };
struct D : B { int j; };

namespace std {
    template <> struct tuple_size<D> : std::integral_constant<int, 2> { };
    template <size_t I> struct tuple_element<I, D> { using type = int; };
}

template <size_t I>
int& get(D& d) {
    if constexpr (I == 0) { return d.i; }
    else                  { return d.j; }
}

template <size_t I>
int const& get(D const& d) {
    if constexpr (I == 0) { return d.i; }
    else                  { return d.j; }
}

template <size_t I>
int&& get(D&& d) {
    if constexpr (I == 0) { return std::move(d).i; }
    else                  { return std::move(d).j; }
}

auto [i,j] = make<D>(); // now ok: we're in case 2 instead of 3
like image 83
Barry Avatar answered Oct 05 '22 10:10

Barry


"same unambiguous public base class" effectively means that, in your entire class hierarchy, there can only be one class that has members in it.

This is sort of like the pre-C++17 rules for aggregate initialization (only there, they explicitly forbid inheritance at all). C++17 allowed base class aggregate initialization, but structured binding (which is conceptually the inverse of aggregate initialization) didn't get the memo.

like image 43
Nicol Bolas Avatar answered Oct 05 '22 10:10

Nicol Bolas