¿Cómo reenviar declarar una clase de plantilla en el espacio de nombres estándar?

131
#ifndef __TEST__
#define __TEST__

namespace std
{
    template<typename T>
    class list;
}

template<typename T>
void Pop(std::list<T> * l)
{
    while(!l->empty())
        l->pop();
}

#endif

y usé esa función en mi main. Me salen errores Por supuesto, sé que hay más parámetros de plantilla parastd::list (asignador, creo). Pero eso no viene al caso. ¿Tengo que conocer la declaración de plantilla completa de una clase de plantilla para poder reenviarla?

EDITAR: No estaba usando un puntero antes, era una referencia. Lo probaré con el puntero.

nakiya
fuente
Y en el caso de la lista, el segundo parámetro es un parámetro predeterminado que esstd::allocator<T>
nakiya
2
uno puede considerar un descuido que el STL no contiene encabezados de declaración hacia adelante. Por otro lado, sus archivos se incluyen con tanta frecuencia que probablemente no produciría ningún beneficio en el tiempo de compilación ...
Matthieu M.
77
__TEST__es un identificador reservado, no lo use .
GManNickG
1
posible duplicado del problema de declaración de avance
iammilind

Respuestas:

146

El problema no es que no pueda declarar hacia adelante una clase de plantilla. Sí, necesita conocer todos los parámetros de la plantilla y sus valores predeterminados para poder declararlo correctamente:

namespace std {
  template<class T, class Allocator = std::allocator<T>>
  class list;
}

Pero hacer incluso una declaración de avance namespace stdestá explícitamente prohibido por el estándar: lo único que se le permite ingresar stdes una especialización de plantilla , comúnmente std::lessen un tipo definido por el usuario. Alguien más puede citar el texto relevante si es necesario.

Solo #include <list>y no te preocupes por eso.

Ah, por cierto, cualquier nombre que contenga guiones bajos dobles está reservado para su uso por la implementación, por lo que debe usar algo como en TEST_Hlugar de __TEST__. No va a generar una advertencia o un error, pero si su programa tiene un choque con un identificador definido por la implementación, no se garantiza que se compile o ejecute correctamente: está mal formado . También están prohibidos los nombres que comienzan con un guión bajo seguido de una letra mayúscula, entre otros. En general, no comience las cosas con guiones bajos a menos que sepa con qué magia está tratando.

Jon Purdy
fuente
44
¿Por qué está prohibido reenviar declarar cosas por namespace stdcierto?
nakiya
44
Consulte esta respuesta ( stackoverflow.com/questions/307343/… ) y la discusión del grupo de noticias vinculado.
Jon Purdy
77
Jon / Nakiya, ¿por qué no usar en #pragma oncelugar de los # ifdef? Es compatible con la mayoría de los compiladores en estos días.
Mark Ingram
11
@ Mark: Porque es #pragma, es por eso. Aunque es una opción.
Jon Purdy
2
Hay una gran cantidad de duplicados de esa pregunta. Solo busque: stackoverflow.com/search?q=pragma+once
Jon Purdy
20

Resolví ese problema.

Estaba implementando una capa OSI (ventana deslizante, Nivel 2) para una simulación de red en C ++ (Eclipse Juno). Tenía marcos (plantilla <class T>) y sus estados (patrón de estado, declaración directa).

La solución es la siguiente:

En el *.cpparchivo, debe incluir el archivo de encabezado que reenvía, es decir

ifndef STATE_H_
#define STATE_H_
#include <stdlib.h>
#include "Frame.h"

template <class T>
class LinkFrame;

using namespace std;

template <class T>
class State {

  protected:
    LinkFrame<int> *myFrame;

}

Su cpp:

#include "State.h"
#include "Frame.h"
#include  "LinkFrame.h"

template <class T>
bool State<T>::replace(Frame<T> *f){

Y ... otra clase.

usuario1638075
fuente
34
Poner cualquiera using namespaceen un archivo de encabezado es una práctica muy mala porque evita que cualquiera que use ese archivo de encabezado pueda usar nombres locales que de otro modo serían válidos. Básicamente derrota todo el punto de los espacios de nombres.
Andy Dent
10

La declaración directa debe tener una lista completa de argumentos de plantilla especificada.

Grozz
fuente
-5

hay una alternativa limitada que puedes usar

encabezamiento:

class std_int_vector;

class A{
    std_int_vector* vector;
public:
    A();
    virtual ~A();
};

cpp:

#include "header.h"
#include <vector>
class std_int_vector: public std::vectror<int> {}

A::A() : vector(new std_int_vector()) {}
[...]

no probado en programas reales, así que espere que no sea perfecto.

Arne
fuente