在C ++中对typedef的前向声明


235

为什么编译器不让我向前声明typedef?

假设这是不可能的,那么使我的包含树变小的最佳实践是什么?

Answers:


170

您可以转发typedef。但是要做

typedef A B;

您必须先转发声明A

class A;

typedef A B;

11
最后+1是因为虽然从技术上讲您不能“ forward-typedef”(即您不能编写“ typedef A;”),但是您几乎可以使用上述技巧来完成OP想要完成的工作。
j_random_hacker

9
但是请注意,如果typedef发生更改,您也可能会更改所有这些前向声明,如果旧的和新的typedef使用具有相同接口的类型,则可能会错过这些声明。
数学

50
通常,这不是有用的解决方案。例如,如果typedef使用前向声明来命名复杂的多级模板类型,则这种方式相当复杂且困难。更不用说它可能需要深入了解默认模板参数中隐藏的实现细节。最终解决方案是一个冗长且无法读取的代码(尤其是当类型来自各种名称空间时),很容易更改原始类型。
亚当·巴杜拉

3
这也显示了“实现细节”(即使不是全部,但仍然...),而向前声明背后的想法是隐藏它们。
亚当·巴杜拉

3
@windfinder:它确实是:template <class T> class A; typedef A <C> B;
milianw 2013年

47

对于像我这样的人,他们希望向前声明使用typedef定义的C样式结构,在某些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;
 }

4
@LittleJohn此解决方案的问题在于,假名_bah不被视为公共API的一部分。请参见正向delcare文件。
user877329 2013年

23

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

长表:

class MyClass;
typedef MyClass myclass_t;

简写:

typedef class MyClass myclass_t;

这与投票最多的问题有何不同?stackoverflow.com/a/804956/931303
Jorge Leitao,

1
@JorgeLeitão您看不出有什么不同吗?它没有显示如何在一行中做到这一点。
Pavel P

17

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

34
维护否否。这种事情迟早会咬在您的怀抱中。
Mark Storer

3
@MarkStorer,至少编译器会捕获任何差异并生成错误。我用Visual C ++验证了这一点。
艾伦(Alan)

很好,但是A由于A定义为空,您如何用这种方式定义字段?
Patrizio Bertoni

10

由于要声明类型,因此需要知道其大小。您可以转发声明该类型的指针,或者typedef指向该类型的指针。

如果确实需要,可以使用pimpl惯用语来减少包含内容。但是,如果要使用类型而不是指针,则编译器必须知道其大小。

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


35
好吧,类类型的前向声明会在不知道其大小的情况下声明这些类型。此外,除了能够定义指向此类不完整类型的指针和引用之外,还可以声明(但未定义)带有参数和/或返回此类类型值的函数。
j_random_hacker

3
抱歉,我认为这不是一个很好的假设。这个答案不重要。typedef是一个前向声明。
Cookie Cookie

6

仅当您打算使用类型本身(在此文件的范围内)而是使用指针或对其的引用时,才可以使用前向声明而不是 full 。 #include

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

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

有趣的是,当使用指针或对classor struct类型的引用时,编译器可以处理不完整的类型,从而省去了向前声明指针类型的需要:

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

2

我有同样的问题,不想弄乱不同文件中的多个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

1

我用继承和构造函数继承(?)代替了typedefusing具体来说)。

原版的

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;

0

正如Bill Kotsias指出的那样,将您的point的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;
};

0

像@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
>>
{
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.