【问题标题】:Forward declaration of a typedef in C++C++ 中 typedef 的前向声明
【发布时间】:2010-10-22 17:42:47
【问题描述】:

为什么编译器不让我转发声明 typedef?

假设这是不可能的,保持我的包含树小的最佳做法是什么?

【问题讨论】:

    标签: c++ typedef forward-declaration


    【解决方案1】:

    你可以做正向 typedef。但是要做

    typedef A B;
    

    你必须先转发声明A:

    class A;
    
    typedef A B;
    

    【讨论】:

    • +1 最后,因为虽然你在技术上不能“forward-typedef”(即你不能写“typedef A;”),但你几乎可以肯定完成 OP 想要完成的事情使用上面的技巧。
    • 但是请注意,如果 typedef 发生变化,您也可能会更改所有这些前向声明,如果新旧 typedef 使用具有相同接口的类型,您可能会错过这些声明。
    • 一般来说,这不是一个有用的解决方案。例如,如果typedef 使用前向声明命名一个复杂的多级模板类型,这种方式相当复杂和困难。更不用说它可能需要深入研究隐藏在默认模板参数中的实现细节。最终的解决方案是一段冗长且不可读的代码(尤其是当类型来自不同的命名空间时),很容易改变原始类型。
    • 这也显示了“实现细节”(即使不完全但仍然......),而前向声明背后的想法是隐藏它们。
    • @windfinder: 它确实:模板 class A;类型定义 A B;
    【解决方案2】:

    对于像我一样希望在某些 c++ 代码中声明使用 typedef 定义的 C 样式结构的人,我找到了如下解决方案...

    // 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;
     }
    

    【讨论】:

    • @LittleJohn 这个解决方案的问题是虚拟名称 _bah 不被视为公共 API 的一部分。请参阅转发 delcare 文件。
    【解决方案3】:

    要“fwd 声明 typedef”,您需要 fwd 声明一个类或结构,然后您可以 typedef 声明类型。编译器可以接受多个相同的 typedef。

    长格式:

    class MyClass;
    typedef MyClass myclass_t;
    

    简写:

    typedef class MyClass myclass_t;
    

    【讨论】:

    • 这与投票最多的问题有何不同? stackoverflow.com/a/804956/931303
    • @JorgeLeitão 你看不出有什么不同吗?它没有显示如何在一行中做到这一点。
    【解决方案4】:

    在 C++(但不是纯 C)中,对一个类型进行两次 typedef 是完全合法的,只要两个定义完全相同:

    // 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);
    

    【讨论】:

    • Maintenance No No. 这种事情迟早会咬你一口。
    • @MarkStorer,至少编译器会发现任何差异并产生错误。我用 Visual C++ 验证了这一点。
    • 很好,但是由于A 的定义是空的,你如何以这种方式定义A 字段?
    • 最新的c标准允许重复typedef
    【解决方案5】:

    因为要声明一个类型,所以需要知道它的大小。你可以转发声明一个指向该类型的指针,或者 typedef 一个指向该类型的指针。

    如果你真的想要,你可以使用 pimpl 成语来减少包含。但是如果你想使用类型而不是指针,编译器必须知道它的大小。

    编辑:j_random_hacker 为这个答案添加了一个重要的限定条件,基本上需要知道大小才能使用类型,但是如果我们只需要知道类型,则可以进行前向声明exists,以便创建指向该类型的指针或引用。由于 OP 没有显示代码,但抱怨它无法编译,我假设(可能正确)OP 正在尝试 use 类型,而不仅仅是引用它。

    【讨论】:

    • 好吧,类类型的前向声明声明这些类型而不知道它们的大小。此外,除了能够定义对此类不完整类型的指针和引用之外,还可以声明(但未定义)接受参数和/或返回此类类型值的函数。
    • 抱歉,我认为这不是一个好的假设。这个答案是题外话。 typedef 前向声明就是这种情况。
    【解决方案6】:

    使用前向声明而不是完整的#includes 只有当您打算使用类型本身(在此文件的范围内)而是使用指针时才可能或参考它。

    要使用类型本身,编译器必须知道它的大小——因此必须看到它的完整声明——因此需要一个完整的#include

    但是,无论指针对象的大小如何,编译器都知道指针或引用的大小,因此前向声明就足够了——它声明了一个类型标识符名称。

    有趣的是,当使用指针或引用classstruct 类型时,编译器可以处理不完整类型,您也无需转发声明指针类型:

    // 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 
    

    【讨论】:

      【解决方案7】:

      我有同样的问题,不想在不同的文件中弄乱多个 typedef,所以我通过继承解决了它:

      曾经:

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

      做过:

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

      工作就像一个魅力。当然,我必须更改任何引用

      BurstBoss::ParticleSystem
      

      简单

      ParticleSystem
      

      【讨论】:

        【解决方案8】:

        我将typedef(具体来说是using)替换为继承和构造函数继承(?)。

        原创

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

        替换

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

        这样我就可以转发声明CallStack

        class CallStack;
        

        【讨论】:

          【解决方案9】:

          正如 Bill Kotsias 所指出的,保持点的 typedef 详细信息私有并向前声明它们的唯一合理方法是使用继承。不过,您可以使用 C++11 做得更好。考虑一下:

          // 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;
          };
          

          【讨论】:

            【解决方案10】:

            和@BillKotsias 一样,我使用了继承,它对我有用。

            我改变了这个混乱(这需要我的声明 *.h 中的所有 boost 标头)

            #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;
            

            进入此声明 (*.h)

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

            并且实现(*.cpp)是

            #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
            >>
            {
            };
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-06-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-06-19
              • 2018-08-13
              • 2023-03-12
              • 1970-01-01
              相关资源
              最近更新 更多