5

C++

How can I put the definition of an inner (nested) class outside its outer (enclosing) class's definition, where the outer class has at least one instance of the inner class as a data member? I searched but the most relevant SO answer I found, Nested Class Definition in source file, does not have an example where the outer class has an inner object as a data member. I followed that answer, as far as declaring but not defining the inner class inside the outer class's definition is concerned, but my code is still broken:

struct Outer
{
    struct Inner;
    Inner myinner;
    Outer() : myinner(2) {}
};

struct Outer::Inner
{
    Inner(int n) : num(n) {}
    int num;
};

int main()
{
    Outer myouter;
}

It gives the error error C2079: 'Outer::myinner' uses undefined struct 'Outer::Inner' in VC11.

And why doesn't the broken code have an effect equivalent to that of the version in which Inner is defined within Outer's definition, like in the following working code?

struct Outer
{
    struct Inner
    {
        Inner(int n) : num(n) {}
        int num;
    } myinner;
    Outer() : myinner(2) {}
};
Community
  • 1
  • 1
CodeBricks
  • 1,771
  • 3
  • 17
  • 37

1 Answers1

6

This is a red flag, but you can do it using a fake template.

template< typename = void >
struct Outer_temp
{
    struct Inner;
    Inner myinner;
    Outer_temp() : myinner(2) {}
};

typedef Outer_temp<> Outer; // Hide template from user.

template< typename v >
struct Outer_temp< v >::Inner
{
    Inner(int n) : num(n) {}
    int num;
};

int main()
{
    Outer myouter;
}

Inner inside the template is a dependent type, so it does not need to be complete as you define an instance, in a member or any other context. It only needs to be complete once the instantiation happens, in this case from main.

I can't imagine a good reason to do this, but there it is.

Nested classes should not be used for the sake of program organization. Nesting suggests a conceptual dependency, "Inner cannot exist except in a context provided by Outer." Although it is common, for example, for a container node class to be nested within the container, this can cause problems. The SCARY idiom is a design style that repudiates such organization and gains improved genericity.

TL;DR: define the two classes independently and link them with a nested typedef.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • +1 for working code. I understand your template syntax. But I don’t know why it works with your fake template but not with my non-template. Can't say I'm a template expert. By `"Inner inside the template is a dependent type, so it does not need to be complete as you define an instance, in a member or any other context. It only needs to be complete once the instantiation happens”` do you mean my `Inner` is an independent type and incomplete at the time it is instantiated within `Outer`? – CodeBricks Nov 21 '13 at 01:31
  • @CodeBricks "dependent" refers to depending on the parameters to the template. "What is inner? It depends what specialization this is." A templates can be instantiated using types declared after itself because templates are allowed to refer to incomplete types and such, as long as they depend on a parameter. I introduced such a false dependency to defer compilation of `Inner myinner` until `main`. – Potatoswatter Nov 21 '13 at 01:37
  • Why can't the declaration of `Inner` in my code defer the compilation until `Outer` sees `Inner`'s definition below `Outer`'s? It seems to work with regular functions. Example: `void g(); void f(){g();} void(g){cout<<"hi\n";}` – CodeBricks Nov 21 '13 at 01:41
  • 1
    @CodeBricks You can call a function with only a declaration, no definition. You cannot instantiate a class until it is defined. If you used `Inner *myinner` inside `Outer` the problem would never have occurred because defining a pointer doesn't require a definition. But pointer arithmetic `++myinner` would require a definition. There are just a lot of rules :P – Potatoswatter Nov 21 '13 at 01:50
  • In your answer, what do you mean by "define the two classes independently and link them with a nested `typedef`"? Do you mean this: `struct Outer { /*...*/ typedef Inner itd; /*...*/}; struct Inner {/*...*/};`? If so, then why? If you want `Inner` to be nonexistent outside `Outer`, how does that help `Inner` be more or less accessible outside `Outer`? Wouldn't accessibility to `Inner`'s members depend only on `Inner`'s access modifiers regardless of which class uses its instance as a data member? Or did you not mean to put the word "nested" there? Were you simply referring to your code above? – CodeBricks Nov 21 '13 at 02:20
  • @CodeBricks This is my point: class nesting doesn't implement interface (in)visibility. It should reflect conceptual possibility in implementation terms. You might compromise a little and let the user have a class they can name but cannot instantiate, using `friend`. – Potatoswatter Nov 21 '13 at 02:27