Почему компилятор не позволяет мне отправить объявление typedef?
Предполагая, что это невозможно, какая наилучшая практика для сохранения маленького дерева включения?
Почему компилятор не позволяет мне отправить объявление typedef?
Предполагая, что это невозможно, какая наилучшая практика для сохранения маленького дерева включения?
Вы можете выполнить форматирование typedef. Но делать
typedef A B;
вы должны сначала направить объявление A
:
class A;
typedef A B;
Для тех, кто из вас, как я, ищущих вперед, объявляет структуру C-стиля, которая была определена с помощью typedef, в некотором С++-коде, я нашел решение, которое выглядит следующим образом...
// 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;
}
В С++ (но не просто 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 ok since they're the same
...
A x;
func(&x);
Чтобы "fwd объявить typedef", вам нужно объявить класс или структуру, а затем вы можете объявить тип typedef. Несколько идентичных typedefs приемлемы компилятором.
длинная форма:
class MyClass;
typedef MyClass myclass_t;
короткая форма:
typedef class MyClass myclass_t;
Чтобы объявить тип, его размер должен быть известен. Вы можете переслать объявление указателя на тип или typedef указатель на тип.
Если вы действительно этого хотите, вы можете использовать идиому pimpl, чтобы поддерживать включенность. Но если вы хотите использовать тип, а не указатель, компилятор должен знать его размер.
Изменить: j_random_hacker добавляет важную квалификацию к этому ответу, в основном, что размер должен знать, чтобы использовать этот тип, но может быть сделано объявление вперед, если нам нужно только знать тип существует, чтобы создать указатели или ссылки на тип. Поскольку ОП не показывал код, но жаловался, что он не будет компилироваться, я предположил (возможно, правильно), что ОП пытался использовать этот тип, а не просто ссылался на него.
Использование форвардных деклараций вместо полного #include
возможно только тогда, когда вы не намереваетесь использовать сам тип (в области этого файла), но указатель или ссылку на него.
Чтобы использовать сам тип, компилятор должен знать его размер - следовательно, его полная декларация должна быть видна, поэтому необходим полный #include
.
Однако размер указателя или ссылки известен компилятору независимо от размера указателя, поэтому достаточно объявления прямого объявления - оно объявляет имя идентификатора типа.
Интересно, что при использовании указателей или ссылок на типы class
или 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
У меня была такая же проблема, я не хотел связываться с несколькими 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
Как отметил Билл Коциас, единственный разумный способ сохранить данные typedef вашей точки в частном порядке, а forward объявить их с наследованием. Вы можете сделать это немного лучше с С++ 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, я использовал наследование, и оно работало на меня.
Я изменил этот беспорядок (который потребовал все заголовки повышения в моем объявлении *.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;