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

Inline member initializer containing pointer to member

在工作中,我正在尝试将一些反射带入我们的代码库。基本上我想要实现的是在数据成员初始化程序的类型中捕获指向数据成员的指针:

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/) 和 Intel C++ XE 14.0 可以编译这段代码,但在使用 MSVC12 时,我收到以下错误消息:

error C2065: 'bar' : undeclared identifier

error C2975: 'dataMember' : invalid template argument for 'Reflect', expected compile-time constant expression

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

所以我的问题是:

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

VC++抱怨的肯定不是问题; [basic.scope.pdecl]/1,6:

The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any), except as noted below.[…]

After the point of declaration of a class member, the member name can be looked up in the scope of its class.

这意味着名称查找没问题。但是,正如@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>{} )();

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

int bar;

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

... 在 bar 的声明中提供初始值设定项时不能。 Demo #2 在 rextester 上。