2

After learning about the fact that nested classes are members of the nesting class and hence have full access to the nesting class's members (at least for C++11, see here), I ran into an issue when trying to create a nested class template:

#include <iostream>

using namespace std;

// #define FORWARD 

class A {

// public: // if FooBar is public, the forward declared version works
protected:
  enum class FooBar { // line 11, mentioned in the error message
    foo,
    bar
  };

protected:
#ifdef FORWARD
  // forward declaration only
  template< FooBar fb = FooBar::foo >
  struct B; 
#else
  // declaration and definition inline
  template< FooBar fb = FooBar::foo >
  struct B{
    void print(){ cout << A::i << (fb==FooBar::foo ? " foo" : " not foo") << endl;};
  };
#endif

public:
  B<>* f;
  B<FooBar::bar>* b;
private:
  static const int i = 42;
}; 

#ifdef FORWARD
// definition of forward declared struct
template< A::FooBar fb>
struct A::B{
  void print(){ cout << A::i << (fb==FooBar::foo ? " foo" : " not foo") << endl; };
}; // line 41, mentioned in the error message
#endif


int main(int argc, char **argv)
{
  A a;
  a.f->print();
  a.b->print();
  return 0;
}

This should (and does) output:

42 foo
42 not foo

Question

Why doesn't this code compile if #define FORWARD is un-commented, i.e. FORWARD is defined?

The error I get (from gcc 4.7.2) is

main.cpp:11:14: error: ‘enum A::FooBar’ is protected
main.cpp:41:2: error: within this context

From an answer to an earlier question I learned that B is a member of A and should have access to the (private) members of it (and it does, it prints A::i). So why isn't A::FooBar accessible in the out-of-class declaration?

Background

This obviously is a minimal example for some other code where header and implementation are split. I would have liked to only forward declare the nested class template B in order to make the interface of class A more readable, as then I could have pushed the template class's implementation to the end of the header file (i.e. the behaviour/setup that one would get by un-commenting the #define FORWARD). So yes, this is a rather cosmetic problem to have--but I believe it shows that I don't understand what is going on and hence I am curious to learn about the but why?.

Community
  • 1
  • 1
hcc23
  • 1,180
  • 11
  • 17

1 Answers1

5

You can reduce your example to simply:

class A
{
private:
  enum FooBar { foo };

public:
  template< FooBar fb = foo >
  struct B;
};

template< A::FooBar >
struct A::B
{
};

A::B<> a;

This is legal, as clarified by DR 580, and is accepted by Clang++, so it looks like G++ doesn't implement that resolution yet.

I've reported this as GCC PR 56248 and also reported Clang PR 15209 because Clang doesn't completely implement DR 580 either.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • Well, I guess I am simply not that skilled to look at the compiler for introducing bugs ;) And thanks for showing me a better (aka smaller) example code--another skill I need to acquire. – hcc23 Feb 08 '13 at 21:02