Прямое объявление typedef в C++
Почему компилятор не позволяет мне объявить typedef?
Предполагая, что это невозможно, каков наилучший способ держать дерево включения небольшим?
11 ответов
Вы можете сделать вперед typedef. Но делать
typedef A B;
Вы должны сначала объявить вперед A
:
class A;
typedef A B;
Для тех из вас, кто, как я, желающих объявить структуру 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 объявить определение типа", вам нужно объявить класс или структуру, а затем вы можете определить объявленный тип. Множественные идентичные определения типов допустимы компилятором.
длинная форма:
class MyClass;
typedef MyClass myclass_t;
Краткая форма:
typedef class MyClass myclass_t;
В 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);
Потому что, чтобы объявить тип, его размер должен быть известен. Вы можете перенаправить объявление указателя на тип или typedef указателя на тип.
Если вы действительно хотите, вы можете использовать идиому pimpl, чтобы уменьшить количество включений. Но если вы хотите использовать тип, а не указатель, компилятор должен знать его размер.
Редактировать: j_random_hacker добавляет важный ответ к этому ответу, в основном, что размер должен быть известен, чтобы использовать тип, но предварительное объявление может быть сделано, если нам нужно только знать, что тип существует, чтобы создать указатели или ссылки на тип. Поскольку OP не показывал код, но жаловался, что он не будет компилироваться, я предположил (вероятно, правильно), что OP пытался использовать тип, а не просто ссылаться на него.
Использование предварительных деклараций вместо полного #include
s возможен только тогда, когда вы не собираетесь использовать сам тип (в области действия этого файла), а указатель или ссылку на него.
Чтобы использовать сам тип, компилятор должен знать его размер - следовательно, должно быть видно его полное объявление - следовательно, полное #include
нужно.
Однако размер указателя или ссылки известен компилятору независимо от размера указателя, поэтому достаточно предварительного объявления - оно объявляет имя идентификатора типа.
Интересно, что при использовании указателя или ссылки на class
или же struct
типы, компилятор может обрабатывать незавершенные типы, избавляя вас от необходимости дополнительно объявлять типы pointee:
// 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
(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;
У меня была та же проблема, я не хотел связываться с несколькими определениями типов в разных файлах, поэтому я решил ее с помощью наследования:
было:
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
Как отметил Билл Коциас, единственный разумный способ сохранить подробности 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;
};
Другое решение состоит в том, чтобы поместить предварительные объявления и определения типов в отдельный заголовок и включить это:
// ForwardDeclarations.h
#pragma once
namespace Foo
{
struct Bar;
typedef Bar Baz;
}
// SomeFile.h
#include "ForwardDeclarations.h"
Foo::Baz baz;
Конечно, на самом деле это не уменьшает количество включаемых файлов, и компилятор все равно должен прочитать этот файл с диска, но, по крайней мере, его содержимое проще, чем полное определение. Вы можете добавить больше предварительных объявлений в тот же файл и включить его в соответствующие места.
Как и @BillKotsias, я использовал наследование, и оно работало на меня.
Я изменил этот беспорядок (который требовал все заголовки повышения в моем объявлении *.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
>>
{
};