指针到“内部结构”成员是否被禁止?

时间:2009-12-18 18:20:30

标签: c++ pointers

我有一个嵌套的结构,我想有一个指向嵌入成员之一的成员指针:

合法吗?

struct InnerStruct
{
    bool c;
};
struct MyStruct {
    bool t;
    bool b;
    InnerStruct inner;
}; 

这样:

MyStruct mystruct;
//...
bool MyStruct::* toto = &MyStruct::b;

没关系,但是:

bool MyStruct::* toto = &MyStruct::inner.c;

不是。任何想法?

感谢

以下是一些细节 是的,它是& MyStruct :: b而不是mystruct :: b; 代码来自自定义RTTI / Property系统。 对于每个指定的类,我们保留一个“Property”数组,包括Ptr-to-member 它的使用方式如下:

//somewhere else in code...
( myBaseClassWithCustomRTTIPointer)->* toto = true;

3 个答案:

答案 0 :(得分:19)

您关心的InnerStruct恰好包含在MyStruct的一个实例中,但这不会影响您获取指向InnerStruct成员的指针。

bool InnerStruct::* toto2 = &InnerStruct::c;

编辑:重读你的问题,我猜你想要定义一个指向外部结构成员的指针,并让它直接指向内部结构的一个成员。这根本不允许。要获取包含在外部结构中的内部结构的成员,您必须创建指向内部结构itselft的指针,然后创建指向其成员的指针。要使用它,您需要取消引用指向成员的指针:

// Pointer to inner member of MyStruct:
InnerStruct MyStruct::* toto = &MyStruct::inner;

// Pointer to c member of InnerStruct:
bool InnerStruct::* toto2 = &InnerStruct::c;

// Dereference both to get to the actual bool:
bool x = mystruct.*toto.*toto2;

答案 1 :(得分:17)

是的,这是禁止的。你并不是第一个提出这个完美逻辑思想的人。在我看来,这是C ++中指向成员的规范中明显的“错误”/“遗漏”之一,但显然委员会没有兴趣进一步开发指向成员的指针(如同大多数“低级”语言特征的情况。)

请注意在语言中已经实现该功能所需的一切。指向a-data-of-a-member的指针与指向直接数据成员的指针完全不同。唯一缺少的是初始化这种指针的语法。但是,委员会显然对引入这样的语法不感兴趣。

从纯粹的形式逻辑的角度来看,这应该是在C ++中允许的

struct Inner {
  int i;
  int j[10];
};

struct Outer {
  int i;
  int j[10];
  Inner inner;
};

Outer o;
int Outer::*p;

p = &Outer::i; // OK
o.*p = 0; // sets `o.i` to 0

p = &Outer::inner.i; // ERROR, but should have been supported
o.*p = 0; // sets `o.inner.i` to 0

p = &Outer::j[0]; // ERROR, but should have been supported
o.*p = 0; // sets `o.j[0]` to 0
// This could have been used to implement something akin to "array type decay" 
// for member pointers

p = &Outer::j[3]; // ERROR, but should have been supported
o.*p = 0; // sets `o.j[3]` to 0

p = &Outer::inner.j[5]; // ERROR, but should have been supported
o.*p = 0; // sets `o.inner.j[5]` to 0

指向数据成员的典型实现只不过是成员从封闭对象开头的字节偏移量。由于所有成员(直接成员和成员成员)最终按顺序排列在内存中,因此成员的成员也可以通过特定的偏移值来识别。这就是我的意思,当我说这个功能的内部工作已经完全实现时,所需要的只是初始化语法。

在C语言中,此功能通过标准offsetof宏获得的显式偏移来模拟。在C中我可以获得offsetof(Outer, inner.i)offsetof(Outer, j[2])。不幸的是,这种能力并没有反映在C ++指针到数据成员中。

答案 2 :(得分:1)

正如AnT的答案所述,这似乎确实是对标准的遗漏以及缺乏正确的语法来表达你想做的事情。我的一位同事前几天遇到了这个问题,并引用了你的问题和答案,证明它无法完成。好吧,我喜欢挑战,是的,它可以做到......但它并不漂亮。

首先,重要的是要意识到指向成员的指针基本上是指向struct [1] 的指针的偏移量。该语言确实有一个与此类似可疑的偏移运算符,有趣的是,它给了我们做我们想要的表达能力。

我们立即遇到的问题是C ++禁止使用转换成员指针。好吧,差不多......我们让工会为此付出了代价。正如我所说,这不是很好!

最后,我们还需要知道要转换为正确的指针类型。

所以不用多说,这里是代码(在gcc和clang上测试):

template <typename C, typename T, /*auto*/size_t P>
union MemberPointerImpl final {
  template <typename U> struct Helper
    { using Type = U C::*; };
  template <typename U> struct Helper<U&>
    { using Type = U C::*; };

  using MemberPointer = typename Helper<T>::Type;

  MemberPointer o;
  size_t i = P; // we can't do "auto i" - argh!
  static_assert(sizeof(i) == sizeof(o));
};

#define MEMBER_POINTER(C, M) \
  ((MemberPointerImpl<__typeof__(C),          \
      decltype(((__typeof__(C)*)nullptr)->M), \
      __builtin_offsetof(__typeof__(C), M)    \
  >{ }).o)

让我们先看一下宏MEMBER_POINTER。它需要两个参数。第一个C是结构,它将成为成员指针的基础。将其包含在__typeof__中并非绝对必要,但允许传递类型或变量。第二个参数M为我们想要指针的成员提供了一个表达式。

MEMBER_POINTER宏从这些参数中提取另外两条信息,并将它们作为参数传递给MemberPointerImpl模板联合。第一部分是指向的成员的类型。这是通过使用我们使用decltype的空指针构造表达式来完成的。第二部分是从基础结构到相关成员的偏移量。

MemberPointerImpl内,我们需要构造MemberPointer类型,它将是宏返回的类型。这是通过一个helper结构来完成的,该结构删除了如果我们的成员是一个数组元素而无法发生的引用,也允许对此进行支持。如果我们将返回值分配给类型不匹配的变量,它还使gcc和clang能够为我们提供一个很好的完全扩展的诊断类型。

因此,要使用MEMBER_POINTER,只需更改代码:

bool MyStruct::* toto = &MyStruct::inner.c;

为:

bool MyStruct::* toto = MEMBER_POINTER(MyStruct, inner.c);

[1]好的,需要注意的是:对于所有体系结构/编译器而言可能并非如此,因此可移植代码编写者现在可以看清了!