关于c ++:Structure Binding:绑定到公共数据成员(继承的基类)

 2020-02-15 

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

即使基类和派生类都具有公共数据成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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();
}

出现错误

1
 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();

根据http://en.cppreference.com/w/cpp/language/structured_binding

案例3:绑定到公共数据成员

E的每个非静态数据成员必须是E的公共直接成员或E的相同明确的公共基础,

无法理解这是什么意思
相同的E公共基础,

演示


"相同的明确公共基类"实际上意味着,在整个类层次结构中,只能有一个包含成员的类。

这有点像C ++ 17之前的聚合初始化规则(仅在那里,它们明确禁止继承)。 C ++ 17允许基类进行聚合初始化,但是结构化绑定(从概念上讲是聚合初始化的反面)没有得到备注。


这里有三个形容词,它们指定三个正交的要求:

  • 相同
  • 明确的
  • 上市
  • 按照顺序,查看反例可能会有所帮助。在所有情况下,均假定存在template T make();


    "相同"的反示例:D有两个成员,但它们不是D相同基数的成员-iB的成员,但是j

    1
    2
    3
    4
    struct B { int i; };
    struct D : B { int j; };

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

    要解决此问题,j需要是B的直接成员,或者i需要是D的直接成员:

    1
    2
    3
    4
    5
    6
    7
    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

    "明确"的反例:D有两个成员,它们都是B的成员,但这是D的模棱两可的基类。

    1
    2
    3
    4
    5
    6
    struct B { int i; };
    struct M1 : B { };
    struct M2 : B { };
    struct D : M1, M2 { };

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

    如果BM1M2virtual基,那么可以:

    1
    2
    3
    4
    5
    6
    struct B { int i; };
    struct M1 : virtual B { };
    struct M2 : virtual B { };
    struct D : M1, M2 { };

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

    "公共"的反例。这是最简单的。如果成员位于私人基地,则无论如何都无法访问它们:

    1
    2
    3
    4
    5
    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

    还应注意,正如TC指出的那样,要求的基础是公开的,而不是成员可访问的。也就是说,使成员可以从私有基础访问仍然不起作用:

    1
    2
    3
    4
    5
    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

    当然,在所有这些反例情况下,仅因为所有成员都不在E的相同,明确的公共基类中,并不意味着它不能与结构化绑定一起使用。这只是意味着您必须自己写出绑定:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    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