Forward declaration of a typedef in C++

C++TypedefForward Declaration

C++ Problem Overview


Why won't the compiler let me forward declare a typedef?

Assuming it's impossible, what's the best practice for keeping my inclusion tree small?

C++ Solutions


Solution 1 - C++

You can do forward typedef. But to do

typedef A B;

you must first forward declare A:

class A;

typedef A B;

Solution 2 - C++

For those of you like me, who are looking to forward declare a C-style struct that was defined using typedef, in some c++ code, I have found a solution that goes as follows...

// a.h
 typedef struct _bah {
    int a;
    int b;
 } bah;

// b.h
 struct _bah;
 typedef _bah bah;

 class foo {
   foo(bah * b);
   foo(bah b);
   bah * mBah;
 };

// b.cpp
 #include "b.h"
 #include "a.h"
 
 foo::foo(bah * b) {
   mBah = b;
 }

 foo::foo(bah b) {
   mBah = &b;
 }

Solution 3 - C++

To "fwd declare a typedef" you need to fwd declare a class or a struct and then you can typedef declared type. Multiple identical typedefs are acceptable by compiler.

long form:

class MyClass;
typedef MyClass myclass_t;

short form:

typedef class MyClass myclass_t;

Solution 4 - C++

In C++ (but not plain C), it's perfectly legal to typedef a type twice, so long as both definitions are completely identical:

// foo.h
struct A{};
typedef A *PA;

// bar.h
struct A;  // forward declare A
typedef A *PA;
void func(PA x);

// baz.cc
#include "bar.h"
#include "foo.h"
// We've now included the definition for PA twice, but it's ok since they're the same
...
A x;
func(&x);

Solution 5 - C++

Because to declare a type, its size needs to be known. You can forward declare a pointer to the type, or typedef a pointer to the type.

If you really want to, you can use the pimpl idiom to keep the includes down. But if you want to use a type, rather than a pointer, the compiler has to know its size.

Edit: j_random_hacker adds an important qualification to this answer, basically that the size needs to be know to use the type, but a forward declaration can be made if we only need to know the type exists, in order to create pointers or references to the type. Since the OP didn't show code, but complained it wouldn't compile, I assumed (probably correctly) that the OP was trying to use the type, not just refer to it.

Solution 6 - C++

Using forward declarations instead of a full #includes is possible only when you are not intending on using the type itself (in this file's scope) but a pointer or reference to it.

To use the type itself, the compiler must know its size - hence its full declaration must be seen - hence a full #include is needed.

However, the size of a pointer or reference is known to the compiler, regardless of the size of the pointee, so a forward declaration is sufficient - it declares a type identifier name.

Interestingly, when using pointer or reference to class or struct types, the compiler can handle incomplete types saving you the need to forward declare the pointee types as well:

// header.h

// Look Ma! No forward declarations!
typedef class A* APtr; // class A is an incomplete type - no fwd. decl. anywhere
typedef class A& ARef;

typedef struct B* BPtr; // struct B is an incomplete type - no fwd. decl. anywhere
typedef struct B& BRef;

// Using the name without the class/struct specifier requires fwd. decl. the type itself.    
class C;         // fwd. decl. type
typedef C* CPtr; // no class/struct specifier 
typedef C& CRef; // no class/struct specifier 

struct D;        // fwd. decl. type
typedef D* DPtr; // no class/struct specifier 
typedef D& DRef; // no class/struct specifier 

Solution 7 - C++

I had the same issue, didn't want to mess with multiple typedefs in different files, so I resolved it with inheritance:

was:

class BurstBoss {

public:

	typedef std::pair<Ogre::ParticleSystem*, bool> ParticleSystem; // removed this with...

did:

class ParticleSystem : public std::pair<Ogre::ParticleSystem*, bool>
{

public:

	ParticleSystem(Ogre::ParticleSystem* system, bool enabled) : std::pair<Ogre::ParticleSystem*, bool>(system, enabled) {
	};
};

Worked like a charm. Of course, I had to change any references from

BurstBoss::ParticleSystem

to simply

ParticleSystem

Solution 8 - C++

I replaced the typedef (using to be specific) with inheritance and constructor inheritance (?).

Original

using CallStack = std::array<StackFrame, MAX_CALLSTACK_DEPTH>;

Replaced

struct CallStack // Not a typedef to allow forward declaration.
  : public std::array<StackFrame, MAX_CALLSTACK_DEPTH>
{
  typedef std::array<StackFrame, MAX_CALLSTACK_DEPTH> Base;
  using Base::Base;
};

This way I was able to forward declare CallStack with:

class CallStack;

Solution 9 - C++

As Bill Kotsias noted, the only reasonable way to keep the typedef details of your point private, and forward declare them is with inheritance. You can do it a bit nicer with C++11 though. Consider this:

// LibraryPublicHeader.h

class Implementation;

class Library
{
...
private:
    Implementation* impl;
};

// LibraryPrivateImplementation.cpp

// This annoyingly does not work:
//
//     typedef std::shared_ptr<Foo> Implementation;

// However this does, and is almost as good.
class Implementation : public std::shared_ptr<Foo>
{
public:
    // C++11 allows us to easily copy all the constructors.
    using shared_ptr::shared_ptr;
};

Solution 10 - C++

Like @BillKotsias, I used inheritance, and it worked for me.

I changed this mess (which required all the boost headers in my declaration *.h)

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>

typedef boost::accumulators::accumulator_set<float,
 boost::accumulators::features<
  boost::accumulators::tag::median,
  boost::accumulators::tag::mean,
  boost::accumulators::tag::min,
  boost::accumulators::tag::max
 >> VanillaAccumulator_t ;
std::unique_ptr<VanillaAccumulator_t> acc;

into this declaration (*.h)

class VanillaAccumulator;
std::unique_ptr<VanillaAccumulator> acc;

and the implementation (*.cpp) was

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>

class VanillaAccumulator : public
  boost::accumulators::accumulator_set<float,
    boost::accumulators::features<
      boost::accumulators::tag::median,
      boost::accumulators::tag::mean,
      boost::accumulators::tag::min,
      boost::accumulators::tag::max
>>
{
};

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser96825View Question on Stackoverflow
Solution 1 - C++Hong JiangView Answer on Stackoverflow
Solution 2 - C++LittleJohnView Answer on Stackoverflow
Solution 3 - C++Pavel PView Answer on Stackoverflow
Solution 4 - C++Adam RosenfieldView Answer on Stackoverflow
Solution 5 - C++tpdiView Answer on Stackoverflow
Solution 6 - C++Adi ShavitView Answer on Stackoverflow
Solution 7 - C++Bill KotsiasView Answer on Stackoverflow
Solution 8 - C++NotinlistView Answer on Stackoverflow
Solution 9 - C++TimmmmView Answer on Stackoverflow
Solution 10 - C++Mark LakataView Answer on Stackoverflow