正如 AnT 的回答所述,这似乎确实是一个标准漏洞,并且缺少表达您想要执行的正确语法。我的一位同事前几天遇到了这个问题,并引用了您的问题和其答案作为证明无法完成此操作的证据。好吧,我喜欢挑战,是的,它可以做到...但并不美观。
首先,重要的是要意识到成员指针基本上是与结构体指针的偏移量[1]。该语言确实有一个 offsetof 运算符,它非常类似于此,并且足够有趣,可以给我们所需的表现力。
我们立即遇到的问题是C++禁止转换成员指针。好吧,几乎可以说...对于此,我们有联合强制转换。正如我所说,这不美观!
最后,我们还需要知道要转换的正确指针类型。
因此,话不多说,这是代码(在 gcc 和 clang 上测试):
template <typename C, typename T, 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;
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
类型,这将是宏返回的内容。这是通过使用一个帮助结构来完成的,该结构删除了引用,如果成员是数组元素,则不会产生帮助性,同时也使得gcc和clang可以在诊断中为我们提供一个很好的完全展开的类型,如果我们将返回值分配给具有不匹配类型的变量。
因此,要使用
MEMBER_POINTER
,只需将代码更改为:
bool MyStruct::* toto = &MyStruct::inner.c;
to:
bool MyStruct::* toto = MEMBER_POINTER(MyStruct, inner.c)
[1] 好的,需要注意的是:这可能并不适用于所有架构/编译器,因此可移植代码编写者现在请离开!
(mystruct.*(&MyStruct::inner.c)) = true
并使其与mystruct.inner.c = true
完全相同。 - Johannes Schaub - litbbool MyStruct::*
类型的指针指向MyStruct::InnerStruct
成员,在 OP 的示例中是完全合理的。然而,语言并不允许这样做。根本没有相应的语法。 - AnT stands with Russia