【问题标题】:How to forward declare a class which is in a namespace如何转发声明位于命名空间中的类
【发布时间】:2013-09-30 20:31:18
【问题描述】:

我正在尝试在头文件中使用前向声明来减少使用的#include 的数量,从而减少用户包含我的头文件时的依赖关系。

但是,我无法转发声明使用命名空间的位置。请参见下面的示例。

文件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__

文件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; 
}

文件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__

实现文件consumer.cpp:

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

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

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

测试文件main.cpp:

#include "consumer.hpp"

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

更新:

这是我非常人为的工作代码,使用下面的答案。

文件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__

文件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; 
}

文件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__

文件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();
}

文件main.cpp:

#include "consumer.hpp"

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

【问题讨论】:

  • 不要使用以两个下划线或一个下划线和一个大写字母开头的名称作为宏——这些是保留的。 stackoverflow.com/questions/17307540/…
  • 这不仅仅是一个命名空间问题。 consumer.cpp 知道有一个类类型 a,但它不知道具体细节。您尝试调用方法 a::talk(),编译器对此一无所知。您仍然需要从 consumer.cpp #include "a.hpp" 以便编译器知道类的完整接口。此#include 将在 .cpp 内部,因此不会通过 consumer.hpp “传播”。

标签: c++ namespaces


【解决方案1】:

对于嵌套命名空间,从 C++17 开始,你可以这样做

namespace ns1::ns2::nsN
{
  class a;
}

【讨论】:

  • class ns1::ns2::nsN::a 怎么样?
  • 松了口气,当一个班级依偎时,旧的形式是如此之长。
【解决方案2】:

除了在其命名空间中前向声明该类(如@billz 所说),请记住在引用前向声明的类时使用(前置)该命名空间,或添加一个@ 987654322@子句:

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

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

参考:Namespaces and Forward Class Declarations

【讨论】:

    【解决方案3】:

    在命名空间ns1 中转发声明类类型a:

    namespace ns1
    {
        class a;
    }
    

    在多级命名空间中转发声明一个类型:

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

    您正在使用a consumer 的成员,这意味着它需要具体类型,您的前向声明不适用于这种情况。

    【讨论】:

    • +1:自从我愤怒地编写 C++ 以来已经过去了 10 年,但我立即知道了答案,只需重新声明命名空间即可。我很高兴我记得这一点,我忘记了很多其他事情。 . .
    • @Thomas,我刚试过,得到了error C3083: 'ns1': the symbol to the left of a '::' must be a type
    • @Thomas 我相信只有在命名空间已经存在时才有效。
    • @Iron 不仅命名空间必须存在,而且必须在其中声明类,在这种情况下,在命名空间之外重新声明类没有什么意义。
    猜你喜欢
    • 2011-03-17
    • 2011-04-22
    • 2013-03-05
    • 2021-02-12
    • 1970-01-01
    • 2012-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多