Прямое объявление typedef в C ++ - PullRequest
217 голосов
/ 30 апреля 2009

Почему компилятор не позволяет мне объявить typedef?

Предполагая, что это невозможно, как лучше всего сохранять дерево включения небольшим?

Ответы [ 9 ]

156 голосов
/ 30 апреля 2009

Вы можете сделать вперед typedef. Но сделать

typedef A B;

Вы должны сначала форвард объявить A:

class A;

typedef A B;
43 голосов
/ 19 января 2010

Для тех из вас, кто, как я, желающих объявить структуру в стиле 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;
 }
17 голосов
/ 30 апреля 2009

В 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);
16 голосов
/ 05 августа 2011

Чтобы "fwd объявить typedef", вам нужно объявить fwd класс или структуру, а затем вы можете typedef объявленного типа. Несколько идентификаторов типов допускаются компилятором.

длинная форма:

class MyClass;
typedef MyClass myclass_t;

краткая форма:

typedef class MyClass myclass_t;
10 голосов
/ 30 апреля 2009

Потому что для объявления типа необходимо знать его размер. Вы можете перенаправить объявление указателя на тип или typedef указателя на тип.

Если вы действительно хотите, вы можете использовать идиому pimpl, чтобы уменьшить количество включений. Но если вы хотите использовать тип, а не указатель, компилятор должен знать его размер.

Редактировать: j_random_hacker добавляет важный ответ к этому ответу, в основном, что размер должен быть известен, чтобы использовал тип, но предварительное объявление может быть сделано, если нам нужно только знать тип существует , чтобы создавать указатели или ссылки на тип. Поскольку OP не показывал код, но жаловался, что он не будет компилироваться, я предположил (вероятно, правильно), что OP пытается использовать тип, а не просто ссылаться на него.

6 голосов
/ 02 января 2014

Использование предварительных объявлений вместо из полных #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 
2 голосов
/ 05 октября 2016

У меня была та же проблема, я не хотел связываться с несколькими определениями типов в разных файлах, поэтому я решил ее с наследованием:

был:

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
0 голосов
/ 12 июня 2018

Как и @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
>>
{
};
0 голосов
/ 23 мая 2017

Как отметил Билл Коциас, единственный разумный способ сохранить подробности 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;
};
...