Declaração de encaminhamento de um typedef em C ++


235

Por que o compilador não me permite encaminhar declarar um typedef?

Supondo que seja impossível, qual é a melhor prática para manter minha árvore de inclusão pequena?

Respostas:


170

Você pode avançar typedef. Mas fazer

typedef A B;

você deve primeiro encaminhar declarar A:

class A;

typedef A B;

11
+1 no final porque, tecnicamente, não é possível "forward-typedef" (ou seja, não é possível escrever "typedef A;"), é quase certo que você pode realizar o que o OP deseja realizar usando seu truque acima.
— Jrandom_hacker 30/04/09

9
Mas esteja ciente de que, se o typedef mudar, você também poderá alterar todas essas declarações avançadas, o que poderá perder se o typedef antigo e o novo usarem tipos com a mesma interface.
— math

50
Em geral, essa não é uma solução útil. Por exemplo, se os typedefnomes de um modelo de modelo multinível complexo usando uma declaração de encaminhamento dessa maneira são bastante complexos e difíceis. Sem mencionar que pode ser necessário mergulhar nos detalhes da implementação ocultos nos argumentos do modelo padrão. E a solução final é um código longo e ilegível (especialmente quando os tipos vêm de vários espaços para nome) muito propensos a alterações no tipo original.
— Adam Badura

3
Além disso, isso mostra "detalhes de implementação" (mesmo que não totalmente, mas ainda ...) enquanto a idéia por trás da declaração de encaminhamento era escondê-los.
— Adam Badura

3
@ windfinder: Faz: template <classe T> classe A; typedef A <C> B;
— milianw

47

Para aqueles que gostam de mim, que desejam encaminhar declarar uma estrutura no estilo C que foi definida usando typedef, em algum código c ++, encontrei uma solução que é a seguinte ...

// 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 O problema com esta solução é que o nome fictício _bah não é considerado parte da API pública. Consulte o arquivo de encaminhamento direto.
— user877329

23

Para "declarar um typedef", você precisa declarar uma classe ou uma estrutura e, em seguida, pode digitar o tipo declarado. Vários typedefs idênticos são aceitáveis ​​pelo compilador.

forma longa:

class MyClass;
typedef MyClass myclass_t;

forma curta:

typedef class MyClass myclass_t;

Como isso é diferente da pergunta mais votada? stackoverflow.com/a/804956/931303
— Jorge Leitao

1
@ JorgeLeitão você não vê como é diferente? Não mostra como fazer isso em uma linha.
— Pavel P

17

No C ++ (mas não no C), é perfeitamente legal digitar um tipo duas vezes, desde que ambas as definições sejam completamente idênticas:

// 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
Manutenção Não. Não. Esse tipo de coisa vai morder você mais cedo ou mais tarde.
— Mark Storer

3
@ MarkStorer, pelo menos o compilador perceberá qualquer diferença e gerará um erro. Eu verifiquei isso com o Visual C ++.
— Alan

Bom, mas como você define os Acampos dessa maneira, uma vez que Aé vazio por definição?
— Patrizio Bertoni

10

Como para declarar um tipo, seu tamanho precisa ser conhecido. Você pode encaminhar declarar um ponteiro para o tipo ou digitar um ponteiro para o tipo.

Se você realmente deseja, pode usar o idioma pimpl para manter as inclusões baixas. Mas se você deseja usar um tipo, e não um ponteiro, o compilador precisa saber seu tamanho.

Edit: j_random_hacker adiciona uma qualificação importante a esta resposta, basicamente que o tamanho precisa ser conhecido para usar o tipo, mas uma declaração direta pode ser feita se precisarmos apenas saber que o tipo existe , a fim de criar ponteiros ou referências para o tipo tipo. Como o OP não mostrou código, mas reclamou que não seria compilado, presumi (provavelmente corretamente) que o OP estava tentando usar o tipo, não apenas se referindo a ele.


35
Bem, declarações avançadas de tipos de classe declaram esses tipos sem o conhecimento de seu tamanho. Além disso, além de poder definir ponteiros e referências a esses tipos incompletos, as funções podem ser declaradas (mas não definidas), que aceitam parâmetros e / ou retornam um valor para esses tipos.
— j_random_hacker

3
Desculpe, não acho que seja uma boa suposição. Esta resposta não vem ao caso. Este é muito o caso de typedef uma declaração direta.
— 22411 Cookie

6

O uso de declarações avançadas em vez de um #includes completo é possível apenas quando você não pretende usar o próprio tipo (no escopo deste arquivo), mas um ponteiro ou referência a ele.

Para usar o próprio tipo, o compilador deve conhecer seu tamanho - portanto, sua declaração completa deve ser vista -, portanto, #includeé necessário um total .

No entanto, o tamanho de um ponteiro ou referência é conhecido pelo compilador, independentemente do tamanho do apontador, portanto, uma declaração de encaminhamento é suficiente - ele declara um nome de identificador de tipo.

Curiosamente, ao usar ponteiro ou referência a classou structtipos, o compilador pode lidar com tipos incompletos, economizando a necessidade de encaminhar também os tipos de ponta de ponte:

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

Eu tinha o mesmo problema, não queria mexer com vários typedefs em arquivos diferentes, então resolvi com herança:

foi:

class BurstBoss {

public:

    typedef std::pair<Ogre::ParticleSystem*, bool> ParticleSystem; // removed this with...

fez:

class ParticleSystem : public std::pair<Ogre::ParticleSystem*, bool>
{

public:

    ParticleSystem(Ogre::ParticleSystem* system, bool enabled) : std::pair<Ogre::ParticleSystem*, bool>(system, enabled) {
    };
};

Funcionou como um encanto. Claro, eu tive que mudar as referências de

BurstBoss::ParticleSystem

simplesmente

ParticleSystem

1

Substituí o typedef( usingpara ser específico) por herança e herança de construtor (?).

Original

using CallStack = std::array<StackFrame, MAX_CALLSTACK_DEPTH>;

Substituído

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

Dessa forma, eu pude encaminhar declarar CallStackcom:

class CallStack;

0

Como Bill Kotsias observou, a única maneira razoável de manter os detalhes digitados do seu ponto em particular, e depois declará-los é com herança. Você pode fazê-lo um pouco melhor com o C ++ 11. Considere isto:

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

Como @BillKotsias, eu usei herança, e funcionou para mim.

Eu mudei essa bagunça (que exigia todos os cabeçalhos de impulso na minha declaração * .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;

nesta declaração (* .h)

class VanillaAccumulator;
std::unique_ptr<VanillaAccumulator> acc;

e a implementação (* .cpp) foi

#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
>>
{
};
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.