Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inline member initializer containing pointer to member

At work, I'm experimenting a bit to bring some reflection into our codebase. Basically what I want to achieve, is to capture a pointer to data-member inside the type of the data-member's initializer:

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

class Foo
{
public:
  int bar = Reflect<Foo, &Foo::bar>{};
};

Although clang 3.4.1 (http://gcc.godbolt.org/) and Intel C++ XE 14.0 are able to compile this piece of code, when using MSVC12 I get the following error message:

error C2065: 'bar' : undeclared identifier

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

Furthermore, gcc 4.9.2 also seems to have trouble with it: http://ideone.com/ZUVOMO.

So my questions are:

  1. Is the above piece of code valid C++11?
  2. If yes, are there any work arounds for the failing compilers?
like image 875
Erik Valkering Avatar asked Apr 01 '15 13:04

Erik Valkering


1 Answers

What VC++ complains about is certainly not a problem; [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.

This implies that the name lookup is fine. However, as pointed out by @hvd in the comments, there are certain ambiguities in the grammar of such constructs.
Presumably GCC parses the above line until the comma:

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.

And bails out once the rest is encountered.


A workaround that makes GCC happy is
    int bar = decltype( Reflect<Foo, &Foo::bar>{} )();

Demo. This does not help with VC++ though, which apparently confuses the point of declaration as indicated by the error message. Thus moving the initializer into a constructor will work:

int bar;

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

... while providing an initializer at the declaration of bar cannot. Demo #2 on rextester.

like image 119
Columbo Avatar answered Oct 12 '22 23:10

Columbo