c++中类型定义的前向声明

为什么编译器不让我向前声明类型定义?

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

194677 次浏览

因为要声明一个类型,就需要知道它的大小。可以前向声明指向该类型的指针,也可以对指向该类型的指针进行类型定义。

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

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

你可以使用正向类型定义。但是要做

typedef A B;

你必须先向前声明A:

class A;


typedef A B;

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

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

对于那些像我一样,希望在一些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;
}

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

长形式:

class MyClass;
typedef MyClass myclass_t;

简式:

typedef class MyClass myclass_t;

只有当你的打算使用类型本身(在这个文件的作用域内)而不是指向它的指针或引用时,才可以使用完整#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

我也有同样的问题,不想在不同的文件中混淆多个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

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

像@BillKotsias一样,我使用了继承,而且它对我很有效。

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

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

我用继承和构造函数继承(?)替换了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;

另一个解决方案是将forward声明和typedefs放在一个单独的头文件中,并包括:

// ForwardDeclarations.h
#pragma once
namespace Foo
{
struct Bar;
typedef Bar Baz;
}


// SomeFile.h
#include "ForwardDeclarations.h"
Foo::Baz baz;

当然,这实际上并没有减少要包含的文件数量,编译器仍然必须从磁盘读取这个文件,但至少内容比完整的定义更简单。您可以在同一个文件中添加更多的前向声明,并将其包含在相关位置。