Search code examples
c++templatesc++11initializerpointer-to-member

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?

Solution

  • 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.