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允许基类进行聚合初始化,但是结构化绑定(从概念上讲是聚合初始化的反面)没有得到备注。
这里有三个形容词,它们指定三个正交的要求:
按照顺序,查看反例可能会有所帮助。在所有情况下,均假定存在
"相同"的反示例:
1 2 3 4 | struct B { int i; }; struct D : B { int j; }; auto [i, j] = make<D>(); // error |
要解决此问题,
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 |
"明确"的反例:
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 |
如果
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 |
当然,在所有这些反例情况下,仅因为所有成员都不在
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 |