Forward declaration of nested types/classes in C++

63,242

Solution 1

You can't do it, it's a hole in the C++ language. You'll have to un-nest at least one of the nested classes.

Solution 2

class IDontControl
{
    class Nested
    {
        Nested(int i);
    };
};

I needed a forward reference like:

class IDontControl::Nested; // But this doesn't work.

My workaround was:

class IDontControl_Nested; // Forward reference to distinct name.

Later when I could use the full definition:

#include <idontcontrol.h>

// I defined the forward ref like this:
class IDontControl_Nested : public IDontControl::Nested
{
    // Needed to make a forwarding constructor here
    IDontControl_Nested(int i) : Nested(i) { }
};

This technique would probably be more trouble than it's worth if there were complicated constructors or other special member functions that weren't inherited smoothly. I could imagine certain template magic reacting badly.

But in my very simple case, it seems to work.

Solution 3

If you really want to avoid #including the nasty header file in your header file, you could do this:

hpp file:

class MyClass
{
public:
    template<typename ThrowAway>
    void doesStuff();
};

cpp file

#include "MyClass.hpp"
#include "Annoying-3rd-party.hpp"

template<> void MyClass::doesStuff<This::Is::An::Embedded::Type>()
{
    // ...
}

But then:

  1. you will have to specify the embedded type at call time (especially if your function does not take any parameters of the embedded type)
  2. your function can not be virtual (because it is a template)

So, yeah, tradeoffs...

Solution 4

If you have access to change the source code of classes C and D, then you can take out class D separately, and enter a synonym for it in class C:

class CD {

};

class C {
public:

    using D = CD;

};

class CD;

Solution 5

I would not call this an answer, but nonetheless an interesting find: If you repeat the declaration of your struct in a namespace called C, everything is fine (in gcc at least). When the class definition of C is found, it seems to silently overwrite the namspace C.

namespace C {
    typedef struct {} D;
}

class A
{
public:
 typedef struct/class {...} B;
...
C::D *someField;
}

class C
{
public:
   typedef struct/class {...} D;
...
   A::B *someField;
}
Share:
63,242

Related videos on Youtube

Calmarius
Author by

Calmarius

Languages I use often: C, PHP, JavaScript, HTML, CSS, MySQL. Languages I use less often: x86 assembly, C++, C# Technologies, formats I know well: x86 architecture, PE file format, ELF file format, OpenGL, OpenAL, SDL. (there may be items I forgot to put on the list.)

Updated on November 30, 2020

Comments

  • Calmarius
    Calmarius over 3 years

    I recently got stuck in a situation like this:

    class A
    {
    public:
        typedef struct/class {…} B;
    …
        C::D *someField;
    }
    
    class C
    {
    public:
        typedef struct/class {…} D;
    …
        A::B *someField;
    }
    

    Usually you can declare a class name:

    class A;
    

    But you can't forward declare a nested type, the following causes compilation error.

    class C::D;
    

    Any ideas?

    • Johannes Schaub - litb
      Johannes Schaub - litb almost 15 years
      Why do you need that? Note that you can forward declare if it's a member of the same class being defined: class X { class Y; Y *a; }; class X::Y { };
    • Albert Wiersch
      Albert Wiersch over 7 years
      This solution worked for me (namespace C { class D; };): stackoverflow.com/questions/22389784/…
    • bitlixi
      bitlixi almost 6 years
      I found a solution link
  • Dolphin
    Dolphin almost 15 years
    I tried this with cygwin gcc and it doesn't compile if you try to reference A.someField. C::D in the class A definition actually refers the the (empty) struct in the namespace, not the struct in the class C (BTW this doesn't compile in MSVC)
  • Calmarius
    Calmarius almost 15 years
    It gives the error: "'class C' redeclared as different kind of symbol"
  • Johannes Schaub - litb
    Johannes Schaub - litb almost 15 years
    Looks like a GCC bug. It seems to think a namespace name can hide a class name in the same scope.
  • Marsh Ray
    Marsh Ray over 12 years
    Thanks for the answer. In my case, they're not my nested classes. I was hoping to avoid a huge library header file dependency with a little forward reference. I wonder if C++11 fixed it?
  • Xeo
    Xeo over 12 years
    In C++11 you can inherit constructors by using basename::basename; in the derived class, thus no problem with complicated ctors.
  • learnvst
    learnvst over 12 years
    Oh. Just what I didn't want google to show up. Thanks anyway for the concise answer.
  • Naftali
    Naftali about 12 years
    What the heck is an hpp file?
  • Maël Nison
    Maël Nison over 11 years
    Same here ... does someone know why it is not possible ? It seems there is valid use cases, and this lack prevents architecture consistency in some situations.
  • Alex Bitek
    Alex Bitek over 11 years
    lol, an .hpp header file is used in C++ projects to distinguish it from a C header file which typically ends with .h. When working with C++ and C in the same project some people prefer .hpp and .cpp for C++ files, to make it explicitly with what type of files their dealing with, and .h and .c for C files.
  • Artem Pisarenko
    Artem Pisarenko over 8 years
    Nice trick, but it will not work if pointer to IDontControl::Nested used within same header (where it forward declared) and accessed from external code which also includes full definition of IDontControl. (Because compiler will not match IDontControl_Nested and IDontControl::Nested). Workaround is to perform static cast.
  • Erik Aronesty
    Erik Aronesty almost 8 years
    You can use friend. And just put a comment in that you're using it to work around the hole in C++.
  • SongWithoutWords
    SongWithoutWords over 7 years
    Whenever I encounter such needless flaws in this ersatz language, I am torn between laughing and crying
  • Mariusz Jaskółka
    Mariusz Jaskółka about 5 years
    It may work, but it is undocumented. The reason why it work is that a::b is mangled the same way, no matter if a is class or namespace.
  • ridderhoff
    ridderhoff about 5 years
    I'd recommend doing the opposite and having the class outside, and just use typedef inside the class
  • Dugi
    Dugi about 5 years
    Doesn't work with Clang or GCC. It says that the outer class was declared as something different than a namespace.