内联成员初始化器包含指向成员的指针

7

我在工作中正在尝试一些方法来使我们的代码库更具反思性。基本上,我想达到的目标是,在数据成员的初始化器类型内捕获指向数据成员的指针:

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' : 未声明的标识符

error C2975: 'dataMember' : 'Reflect'的无效模板参数,预期为编译时常量表达式

此外,gcc 4.9.2似乎也有问题:http://ideone.com/ZUVOMO
所以我的问题是:
  1. 上述代码片段是否有效的C ++11?
  2. 如果是,是否有任何解决编译器失败的方法?
1个回答

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上查看Demo #2

1
据我所知,这是一个未解决的语言问题。不清楚逗号何时分隔模板参数,何时分隔两个成员。int a=b<c,d=e>::f; 可以声明成员 a 并将其初始化为 b<c,d=e>::f,也可以声明成员 a 初始化为 b<c,并且成员 d 初始化为 e>::f。这还受到 NSDMI 可能引用类的尚未声明的成员的复杂性的影响。 - user743382
@hvd 我以为语法已经明确表述了这一点。好主意。你能找到与此相关的 DR 吗? - Columbo
对于函数默认参数的相关问题,可以参考#325。也许这就是我记得的内容,或许没有单独针对NSDMI的问题。 - user743382
@hvd 我相当确定 d=e 不能是一个有效的模板参数。然而,这是一个从解析器开始的语法问题,不是吗? - Columbo
是的,条件表达式可以是主表达式,而主表达式可以是带括号的表达式(允许赋值)。 - user743382
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接