forward declaration

From Cppwiki

Jump to: navigation, search

Forward declaration is the technique of declaring a symbol in order to be able to use it without needing its definition.

Macros, enumerated types, or typedefs may not be forward-declared.

Contents

Functions

A function declaration makes available the full signature of the function, which is all that one needs to use that function.

  // declare f
void f();
 
// use f
int main() {
  f();
}
 
// define f
void f() {
  std::cout << "hello world!\n";
}

Classes

A class declaration makes available its name, but not its base classes or members, nor its size.

class MyClass;
 
// extern and static member declarations
extern MyClass g_obj;
struct A { static MyClass s_obj; };
 
// typedefs & friendships
typedef int MyClass::*PTM;
typedef void (MyClass::*PTMF) ();
class OtherClass { friend class MyClass; };
 
// function declarations
MyClass  Copy(MyClass x);
MyClass *GetPtr();
PTM      GetPTM();
PTMF     GetPTMF();
 
// pointers, references, pointers-to-member
MyClass *p   = GetPtr();
MyClass &ref = *p;
PTM      p_m = GetPTM();
PTMF     p_f = GetPTMF();
 
void UsePTM() {
  (p->*p_m) = 42;
  assert((ref.*p_m) == 42):
  (p->*p_f)();
  (ref.*p_f)();
}

Cases where forward declaration is insufficient

class MyClass {
public:
  void Go();
  int m_field;
};
 
// sizeof (needs size)
const int n = sizeof MyClass;
 
// extern and static member definitions (size, constructor, destructor)
MyClass g_obj;
MyClass A::s_obj;
 
// nonstatic member declaration (size)
struct B { MyClass s_obj; };
 
// function definition that passes/returns by value (size, copy constructor, destructor)
MyClass DoSomething(MyClass x) { return x; }
 
// refer to members by name
void UseMembers() {
  p->Go();
  ref.Go();
  p_m = &MyClass::m_field;
  p_f = &MyClass::Go;
}

Practical example: chicken and egg

  // declare Egg
class Egg;
 
class Chicken {
public:
  // use Egg
  Chicken(const Egg &egg);
};
 
// define Egg
class Egg {
 public:
  Egg(const Chicken &mother, const Chicken &father);
};


Objects

  // declare
extern int n;
class A {
public:
  static int m_n;
};
 
// use
int main() {
  assert(n == 42);
  assert(A::m_n == 42);
}
 
// define
int n = 42;
int A::m_n = 42;


Templates

Templates can be declared and defined separately. However, templates are almost always instantiated at the point of use, and instantiation requires the definition. Therefore, in general, the definitions must be located in a header file and included before use.

  • s_fwd.h:
#ifndef S_FWD_H
#define S_FWD_H
 
// declare
template <typename T> class S;
template <typename T> void f(const S<T> &);
 
#endif
  • s.h:
#ifndef S_H
#define S_H
 
#include "s_fwd.h"
 
// define f
template <typename T>
void f(const S<T> &obj)
{
  obj.f();
}
 
// define S / declare S::f
template <typename T>
class S
{
public:
  void f() const;
};
 
// define S::f
template <typename T>
void S<T>::f() const
{
  std::cout << "hello world!\n";
};
 
#endif
  • main.cpp:
#include "S.h"
 
int main()
{
  S<int> x;
  f(x);
}

If your template is only instantiated for a few types, you can use explicit template instantiation to work around this.

  • s.h:
#ifndef S_H
#define S_H
 
#include "s_fwd.h"
 
// define S / declare S::f
template <typename T>
class S
{
public:
  void f() const;
};
 
#endif
  • s.cpp:
#include "s.h"
 
// define f
template <typename T>
void f(const S<T> &obj)
{
  obj.f();
}
 
// define S::f
template <typename T>
void S<T>::f() const
{
  std::cout << "hello world!\n";
};
 
// define S<int>::f
template <> class S<int>;
// define f<int>
template <> void f<int>();
Personal tools