88

I am trying to use forward declarations in header files to reduce the number of #include used and hence reduce dependencies when users include my header file.

However, I am unable to forward declare where namespaces are used. See example below.

File a.hpp:

#ifndef __A_HPP__
#define __A_HPP__

namespace ns1 {

   class a {
   public:
      a(const char* const msg);

      void talk() const;

   private:
      const char* const msg_;
   };
}

#endif //__A_HPP__

File a.cpp:

#include <iostream>

#include "a.hpp"

using namespace ns1;

a::a(const char* const msg) : msg_(msg) {}

void a::talk() const { 
   std::cout << msg_ << std::endl; 
}

File consumer.hpp:

#ifndef __CONSUMER_HPP__
#define __CONSUMER_HPP__

// How can I forward declare a class which uses a namespace
//doing this below results in error C2653: 'ns1' : is not a class or namespace name
// Works with no namespace or if I use using namespace ns1 in header file
// but I am trying to reduce any dependencies in this header file
class ns1::a;

class consumer
{
public:
   consumer(const char* const text) : a_(text) {}
   void chat() const;

private:
   a& a_;
};

#endif // __CONSUMER_HPP__

Implementation file consumer.cpp:

#include "consumer.hpp"
#include "a.hpp"

consumer::consumer(const char* const text) : a_(text) {}

void consumer::chat() const {
   a_.talk();
}

Test file main.cpp:

#include "consumer.hpp"

int main() {
   consumer c("My message");
   c.chat();
   return 0;
}

UPDATE:

Here is my very contrived working code using the answer below.

File a.hpp:

#ifndef A_HPP__
#define A_HPP__

#include <string>

namespace ns1 {

   class a {
   public:
      void set_message(const std::string& msg);
      void talk() const;

   private:
      std::string msg_;
   };

} //namespace

#endif //A_HPP__

File a.cpp:

#include <iostream>
#include "a.hpp"

void ns1::a::set_message(const std::string& msg) {
    msg_ = msg;
}
void ns1::a::talk() const { 
   std::cout << msg_ << std::endl; 
}

File consumer.hpp:

#ifndef CONSUMER_HPP__
#define CONSUMER_HPP__

namespace ns1
{
   class a;
}

class consumer
{
public:
   consumer(const char* text);
   ~consumer();
   void chat() const;

private:
   ns1::a* a_;
};

#endif // CONSUMER_HPP__

File consumer.cpp:

#include "a.hpp"
#include "consumer.hpp"

consumer::consumer(const char* text) {
   a_ = new ns1::a;
   a_->set_message(text);
}
consumer::~consumer() {
   delete a_;
}
void consumer::chat() const {
   a_->talk();
}

File main.cpp:

#include "consumer.hpp"

int main() {
   consumer c("My message");
   c.chat();
   return 0;
}
Dada
  • 6,313
  • 7
  • 24
  • 43
Angus Comber
  • 9,316
  • 14
  • 59
  • 107
  • 8
    Don't use names starting with two underscores or an underscore and a capital letter for macros -- these are reserved. http://stackoverflow.com/questions/17307540/include-guard-conventions-in-c/17307796#17307796 – Benjamin Bannier Sep 25 '13 at 09:59
  • this is not just a namespace issue. consumer.cpp is aware that there is a class type a, but it is not aware of the specifics. You try to invoke the method a::talk(), which the compiler knows nothing about. You still need to #include "a.hpp" from consumer.cpp so the compiler is aware of the classes full interface. This #include will be internal to the .cpp hence will not be "spread around" through consumer.hpp. – Avi Perel Sep 25 '13 at 10:35

3 Answers3

123

To forward declare class type a in a namespace ns1:

namespace ns1
{
    class a;
}

To forward declare a type in multiple level of namespaces:

namespace ns1
{
  namespace ns2
  {
    //....
     namespace nsN
     {
        class a;
     }
    //....    
  }
}

Your are using a a member of consumer which means it needs concrete type, your forward declaration won't work for this case.

Community
  • 1
  • 1
billz
  • 44,644
  • 9
  • 83
  • 100
  • 4
    +1: Its easily 10 years since I wrote C++ in anger but I immediately knew the answer to this, simply re-declare the namespace. I'm quite pleased I remembered that, I've forgotten so much else . . . – Binary Worrier Sep 25 '13 at 09:59
  • 3
    @Thomas, I just tried that and got `error C3083: 'ns1': the symbol to the left of a '::' must be a type` – Dialecticus Sep 25 '13 at 10:04
  • 2
    @Thomas I believe that only works if the namespace already exists. – Iron Savior Sep 25 '13 at 10:13
  • 3
    @Iron Not only that namespace must exist, but the class must be declared in it, in which case there's little point redeclaring the class outside of namespace. – Dialecticus Sep 25 '13 at 10:17
32

For nested namespaces, since C++17, you can do

namespace ns1::ns2::nsN
{
  class a;
}
user2193043
  • 431
  • 4
  • 3
4

Apart to forward-declare the class from within its namespace (as @billz says), remember to either use (prepend) that namespace when referring to the forward-declared class, or add a using clause:

// B.h
namespace Y { class A; } // full declaration of
// class A elsewhere

namespace X {
    using Y::A;   // <------------- [!]
    class B {
        A* a; // Y::A
    };
}

Ref: Namespaces and Forward Class Declarations

Campa
  • 4,267
  • 3
  • 37
  • 42