zne*_*eak 69 c++ pointer-to-member
C++很酷的一点是,它允许您创建指向成员类型的变量.最常见的用例似乎是获取指向方法的指针:
struct foo
{
int x() { return 5; }
};
int (foo::*ptr)() = &foo::x;
foo myFoo;
cout << (myFoo.*ptr)() << '\n'; // prints "5"
Run Code Online (Sandbox Code Playgroud)
然而,乱搞,我意识到他们也可以指向成员变量:
struct foo
{
int y;
};
int foo::*ptr = &foo::y;
foo myFoo;
myFoo.*ptr = 5;
cout << myFoo.y << '\n'; // prints "5"
Run Code Online (Sandbox Code Playgroud)
这很漂亮.它引导我进一步实验:如果你能得到指向结构子成员的指针怎么办?
struct foo
{
int y;
};
struct bar
{
foo aFoo;
};
int bar::*foo::*ptr;
Run Code Online (Sandbox Code Playgroud)
这实际上编译.
但是,我不知道如何分配任何有用的东西.以下都不起作用:
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::*'
Run Code Online (Sandbox Code Playgroud)
此外,根据这产生的错误,似乎这种类型并不是我想到的:
int bar::*foo::*ptr = nullptr;
bar myBar;
myBar.*ptr = 4; // pointer to member type ‘int bar::*’ incompatible
// with object type ‘bar’
Run Code Online (Sandbox Code Playgroud)
这个概念似乎逃避了我.显然,我不能排除它只是以一种完全不同于我期望的方式进行解析.
请问有谁请你解释一下int bar::*foo::*究竟是什么?为什么gcc告诉我指向成员的指针bar与bar对象不兼容?我将如何使用int bar::*foo::*,以及如何构建有效的?
Joh*_*nck 54
这是初始化这种怪物的"有效"方式:
struct bar;
struct foo
{
int y;
int bar::* whatever;
};
struct bar
{
foo aFoo;
};
int bar::* foo::* ptr = &foo::whatever;
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,ptr是指向foo(foo::*从右到左阅读)成员的指针,其中该成员本身是指向bar(bar::*)成员的指针,其中该成员是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;
Run Code Online (Sandbox Code Playgroud)
jro*_*rok 19
这将是指向数据成员的指针,数据成员本身是指向数据成员(int成员bar)的指针.
不要问我它实际上有用的是什么 - 我的头旋转了一点:)
编辑:这是一个完整的例子:
#include <iostream>
struct bar {
int i;
};
struct foo {
int bar::* p;
};
int main()
{
bar b;
b.i = 42;
foo f;
f.p = &bar::i;
int bar::*foo::*ptr = &foo::p;
std::cout << (b.*(f.*ptr));
}
Run Code Online (Sandbox Code Playgroud)
产量当然是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);
}
Run Code Online (Sandbox Code Playgroud)
T.C*_*.C. 13
我们来解析声明int bar::*foo::*ptr;.
§8.3.3[dcl.mptr]/p1:
在具有表格的声明
T D中DRun Code Online (Sandbox Code Playgroud)nested-name-specifier * attribute-specifier-seq_opt cv-qualifier-seq_opt D1并且nested-name-specifier表示一个类,声明中标识符的类型
T D1是" derived-declarator-type-listT",然后标识符的类型D是" derived-declarator-type-list cv-qualifier" -seq指向类型为" 的类nested-name-specifier的成员T".
第1步:这是上述形式的声明,其中T= int,nested-name-specifier = bar::和D1 = foo::* ptr.我们首先看一下声明T D1,或者int foo::* ptr.
第2步:我们再次应用相同的规则.int foo::* ptr是上述形式的声明,其中T= int,nested-name-specifier = foo::和D1= ptr.显然,在该标识符的类型int ptr是" int",所以该标识符的类型ptr的声明int foo::* ptr是"指针类的成员foo类型的int".
第3步.我们回到原始声明; 在标识符的类型T D1(int foo::* ptr)是"指针类的成员foo类型的int"每步2,所以导出的说明符类型列表是"指针类的成员foo类型的".替换告诉我们这个声明声明ptr是"指向类foo类型成员的类bar的成员指针int".
希望你永远不需要使用这样的怪物.