包含指向成员的指针的内联成员初始值设定项

Inline member initializer containing pointer to member

本文关键字:成员 指针 包含指      更新时间:2023-10-16

在工作中,我正在进行一些实验,以便在我们的代码库中引入一些反射。基本上,我想要实现的是在数据成员的初始值设定项的类型中捕获一个指向数据成员的指针:

template<class Class, int Class::*dataMember>
struct Reflect
{
  operator int() {return 0;}
};
class Foo
{
public:
  int bar = Reflect<Foo, &Foo::bar>{};
};

尽管clang 3.4.1(http://gcc.godbolt.org/)和英特尔C++XE 14.0能够编译这段代码,当使用MSVC12时,我收到以下错误消息:

错误C2065:"bar":未声明的标识符

错误C2975:"dataMember":"Reflect"的模板参数无效,应为编译时常量表达式

此外,gcc 4.9.2似乎也有问题:http://ideone.com/ZUVOMO.

所以我的问题是:

  1. 上面的代码是有效的C++11吗
  2. 如果是,对于失败的编译器,是否有任何解决方法

VC++抱怨的当然不是问题;【basic.scope.pdecl】/1,6:

名称的声明点紧接在其完成之后声明符(第8条)及其初始值设定项之前(如果有),除非如下所述。[…]

在类成员的声明点之后,成员名称可以在其类的范围内查找

这意味着名称查找是可以的。然而,正如@hvd在评论中指出的那样,这种结构的语法存在一定的歧义
大概GCC会解析上面的行,直到逗号:

int bar = Reflect<Foo,
// at this point Reflect < Foo can be a perfectly fine relational-expression.
// stuff after the comma could be a declarator for a second member.

一旦遇到其他人就会跳伞。


一个让GCC满意的变通方法是

    int bar = decltype( Reflect<Foo, &Foo::bar>{} )();

演示。不过,这对VC++没有帮助,它显然混淆了错误消息所指示的声明点。因此,将初始值设定项移动到构造函数中是可行的:

int bar;
Foo() : bar( Reflect<Foo, &Foo::bar>{} ) {}
// (also works for GCC)

而在bar的声明处提供初始值设定项则不能rextester上的演示#2