什么是'int foo :: * bar :: *`?

C ++的一个很酷的事情是它可以让你创build指向成员types的variables。 最常见的用例似乎是获得一个方法的指针:

struct foo { int x() { return 5; } }; int (foo::*ptr)() = &foo::x; foo myFoo; cout << (myFoo.*ptr)() << '\n'; // prints "5" 

然而,乱搞,我意识到,他们可以指向成员variables:

 struct foo { int y; }; int foo::*ptr = &foo::y; foo myFoo; myFoo.*ptr = 5; cout << myFoo.y << '\n'; // prints "5" 

这很好。 这引导我进一步的实验:如果你能得到一个结构的子成员的指针?

 struct foo { int y; }; struct bar { foo aFoo; }; int bar::*foo::*ptr; 

这实际上编译 。

但是,我不知道如何分配任何有用的东西。 以下任何一项工作:

 int bar::*foo::*ptr = &bar::foo::y; // no member named "foo" in "bar" int bar::*foo::*ptr = &bar::aFoo::y; // no member named "aFoo" in "bar" (??) int bar::*foo::*ptr = &foo::y; // can't init 'int bar::*foo::*' with 'int foo::*' 

而且,根据这个错误,看起来这种types并不是我想到的:

 int bar::*foo::*ptr = nullptr; bar myBar; myBar.*ptr = 4; // pointer to member type 'int bar::*' incompatible // with object type 'bar' 

看来,这个概念回避我。 很明显,我不能排除它只是以与我所期望的完全不同的方式进行parsing。

任何人都可以请解释一下int bar::*foo::*究竟是什么? 为什么gcc告诉我一个指向bar成员的指针与bar对象不兼容? 我将如何使用int bar::*foo::* ,以及如何构造一个有效的?

这是初始化这种怪物的“有效”方法:

 struct bar; struct foo { int y; int bar::* whatever; }; struct bar { foo aFoo; }; int bar::* foo::* ptr = &foo::whatever; 

正如我们所看到的, ptr是指向foofoo::* ,从右到左读)的成员的指针,其中该成员本身是指向barbar::* )成员的指针,其中该成员是INT。

我将如何使用int bar :: * foo :: *

你不会,希望! 但如果你受到胁迫,试试这个!

 struct bar { foo aFoo; int really; }; int bar::* foo::* ptr = &foo::whatever; foo fleh; fleh.whatever = &bar::really; bar blah; blah.*(fleh.*ptr) = 42; std::cout << blah.really << std::endl; 

这将是一个指向数据成员的指针,它本身是指向数据成员( barint成员)的指针。

不要问我什么是有用的 – 我的头旋转一点:)

编辑:这是一个完整的例子:

 #include <iostream> struct bar { int i; }; struct foo { int bar::* p; }; int main() { bar b; bi = 42; foo f; fp = &bar::i; int bar::*foo::*ptr = &foo::p; std::cout << (b.*(f.*ptr)); } 

产量当然是42。

它可以变得更有趣 – 下面是一些指向成员函数的指针,返回指向成员函数的指针:

 #include <iostream> struct bar { int f_bar(int i) { return i; }; }; struct foo { int(bar::*f_foo())(int) { return &bar::f_bar; } }; int main() { int(bar::*((foo::*ptr)()))(int) = &foo::f_foo; bar b; foo f; std::cout << (b.*((f.*ptr)()))(42); } 

让我们分析一下这个声明int bar::*foo::*ptr;

§8.3.3[dcl.mptr] / p1:

D有forms的声明TD

 nested-name-specifier * attribute-specifier-seq_opt cv-qualifier-seq_opt D1 

并且嵌套名称说明符表示一个类,并且声明T D1中的标识符的types是“ 派生 – 声明types列表 T ”,则D的标识符的types是“ derived-declarator-type-列出 cv-qualifier-seq指向typesT的类嵌套名称说明符成员的指针“。

  • 第一步:这是上面的表单的声明,其中T = int, 嵌套名称说明符 = bar::D1 = foo::* ptr 。 我们首先看看声明T D1 ,或者int foo::* ptr

  • 第二步:我们再次应用相同的规则。 int foo::* ptr是上述forms的声明,其中T = int, nested-name-specifier = foo::D1 = ptr 。 显然, int ptr中的标识符的types是“ int ”,所以声明int foo::* ptr中的标识符ptr的types是“指向types为int的类的成员的指针”。

  • 第3步。我们回到原来的声明。 T D1int foo::* ptr )中的标识符的types是“types为int成员的指针”,所以派生的声明types列表是“指向类foo的成员的指针types”。 replace告诉我们,这个声明声明ptr是“types指针foo类成员的指针,指向types为int的类的成员”。

希望你永远不需要使用这样的怪物。

如果有人想知道,你不能创build一个嵌套多层的指针。 这样做的原因是,所有指针指向的成员实际上比第一眼看起来更复杂; 它们并不仅仅包含特定成员的特定偏移量。

由于虚拟inheritance等原因,使用简单的偏移量不起作用。 基本上可能会发生这样的情况,即使在单一types中,实例之间特定字段的偏移也是不同的,因此指向成员的分辨率需要在运行时完成。 这主要是由于标准没有指定非PODtypes的内部布局如何工作,所以无法使其静态工作。

如果是这种情况,那么用一个普通的指向成员的指针来做两级深度的parsing是不可能的,但是需要编译器来产生一个指针,使得它包含一个深度为一个深度的指针的信息的两倍。

我想象,因为指向成员的指针并不常见,所以当你仍然可以使用多个指针来达到相同的结果时,实际上不需要创build一个允许设置多层深度成员的语法。

首先,为了帮助“可读性”,你可以使用括号(编译工作):

 struct bar; struct foo { int y; int (bar:: *whatever); // whatever is a pointer upon an int member of bar. }; struct bar { foo aFoo; }; // ptr is a pointer upon a member of foo which points upon an int member of bar. int (bar:: *(foo:: *ptr)) = &foo::whatever; 

注意

int(bar :: * whatever)

相当于

int(*无论)

对酒吧的会员资格有约束。

至于

int(bar :: *(foo :: * ptr))

,这相当于

int(*(* ptr))

对foo和bar的成员有两个限制。

他们只是指针。 他们不检查bar或foo是否真的有一个兼容的成员,因为这将阻止使用class bar的前向声明,并且class bar不检查其他类是否通过指针引用其成员。 此外,您可能还需要引用一个不透明的类(即在一个单独的单元中定义一个类栏)。

那么用处呢? 也许对于C ++reflection来设置/通过类包装来获取类成员的值?

 template< typename Class, typename Type > struct ClassMember { using MemberPointer = Type (Class:: *); MemberPointer member; ClassMember(MemberPointer member) : member(member) {} void operator()(Class & object, Type value) { object.*member = value; } Type operator()(Class & object) { return object.*member; } }; template< typename Class, typename Type > ClassMember< Class, Type > MakeClassMember(Type(Class:: *member)) { return ClassMember< Class, Type >(member); } struct Rectangle { double width; double height; Rectangle(double width = 0., double height = 0.) : width(width), height(height) {} }; int main(int argc, char const *argv[]) { auto r = Rectangle(2., 1.); auto w = MakeClassMember(&Rectangle::width); auto h = MakeClassMember(&Rectangle::height); w(r, 3.); h(r, 2.); printf("Rectangle(%f, %f)\n", w(r), h(r)); return 0; } 

当然,这个例子并没有显示双重成员指针的具体用法,因为在这里我没有看到一个简单的方法来说明这个问题,或者是从概念上讲这么做的一个很好的理由。

Interesting Posts