2010-10-18 18 views
83

Deseo tener una clase que no sea de plantilla con un constructor de plantilla sin argumentos.constructor de plantilla C++

Por lo que yo entiendo, es imposible tenerlo (ya que entraría en conflicto con el constructor por defecto - Estoy en lo cierto?), y la solución es la siguiente:

class A{ 
    template <typename U> A(U* dummy) { 
    // Do something 
    } 
}; 

Tal vez hay una mejor alternativa para esto (o una mejor solución)?

+3

¿Para qué lo necesita? –

+0

Me gustaría repetir la pregunta de Johannes. ¿Por qué? Podría haber una técnica mejor si comprendemos lo que estás tratando de hacer. –

+2

@Loki Sería bueno tenerlo si se genera algo a partir de una secuencia de entradas (como el constructor del iterador con plantilla del vector). – VF1

Respuesta

82

No hay forma de especificar explícitamente los argumentos de la plantilla al llamar a una plantilla de constructor, por lo que deben deducirse a través de la deducción de argumento. Esto es porque si usted dice:

Foo<int> f = Foo<int>(); 

El <int> es la lista de argumentos de plantilla para el tipo Foo, no por su constructor. No hay ningún lugar para la lista de argumentos de la plantilla del constructor.

Incluso con su solución, todavía tiene que pasar un argumento para llamar a esa plantilla de constructor. No está del todo claro lo que estás tratando de lograr.

+1

PUEDE ser posible con una llamada de constructor cualificada, pero parece que no funciona: Foo :: Foo (); – John

+0

Pero la inferencia de tipo automática todavía es posible. Siempre que esté satisfecho con la inferencia de tipo automática, puede usar un constructor de plantilla (de una clase que no sea de plantilla). – updogliu

+0

@updogliu: Absolutamente. Pero, la pregunta es acerca de "un constructor de plantilla sin argumentos". Si no hay argumentos de función, no se pueden deducir argumentos de plantilla. –

26

Puede crear una función de fábrica de plantilla:

class Foo 
{ 
public: 
    template <class T> static Foo* create() // could also return by value, or a smart pointer 
    { 
     return new Foo(...); 
    } 
...   
}; 
+0

Pero, ¿qué pasa con los objetos normales? –

+4

@Martin: creo que esto también podría devolver por valor (un no puntero). RVO debería encargarse de eliminar la copia de todos modos. – UncleBens

+1

create() no tiene que hacer la asignación dinámica. Solo 'devuelve a Foo (...);' Gracias @Samuel_xL. Esto resultó ser una gran idea para mí. – NoahR

21

Por lo que yo entiendo, es imposible tenerlo (ya que entraría en conflicto con el constructor por defecto - Estoy en lo correcto?)

Usted está equivocado. No entra en conflicto de ninguna manera. No puedes llamarlo nunca.

+1

Eso es quisquilloso ;-) pero muy alemán: P –

+0

1. En realidad, puede llamar a la plantilla ctor: 'int * p; A a (p) '. 2. Prejuicio o idealización ;-) No hay John Doe. No hay nada típico alemán ... y definitivamente no en las palabras de Johannes. Si suenan sin sentido, simplemente porque C++ no tiene humor. –

+1

@AndreasSpindler Quise decir que nunca puede llamar al constructor si no tiene argumentos, pero sigue siendo un parámetro de plantilla (se aplica solo a C++ 03. C++ 11 ha introducido el parámetro de plantilla predeterminado, que permitiría llamarlo, por supuesto, si el ctor tiene argumentos predeterminados ...). –

15

Algunos puntos:

  • Si se declara cualquier constructor (que incluye una plantilla uno), el compilador se abstengan de declarar un constructor por defecto.
  • A menos que se declara un constructor de copia (para la clase X que toma una X o X& o X const &) el compilador generará el constructor de copia defecto.
  • Si proporciona un constructor plantilla para la clase X que toma T const & o T o T& entonces el compilador, sin embargo, generar una por defecto no moldeado constructor de copia, a pesar de que usted puede pensar que no debería hacerlo porque cuando T = X la declaración coincide con la declaración copy-constructor.
  • En este último caso, es posible que desee proporcionar un constructor de copias sin plantilla junto con el de plantilla. Ellos no entrarán en conflicto. Cuando se pasa X, se llamará al no seleccionado.De lo contrario la plantilla de

HTH

+1

Lo curioso es que si declara algo como plantilla X (const X &); entonces aún no será reconocido como constructor de copia, y se creará el constructor de copia estándar. –

+0

@j_kibik: Sí, lo mismo ocurre con la asignación con plantilla –

+1

... y las ctors predeterminadas. Una clase declarada como 'struct S {template S() {}};' declara un "ctor inalcanzable". No hay forma de deducir o pasar la 'T'. No obstante, no se genera un ctor predeterminado porque parece existir un ctor definido por el usuario. –

0

intentar hacer algo como

template<class T, int i> class A{ 

    A(){ 
      A(this) 
    } 

    A(A<int, 1>* a){ 
      //do something 
    } 
    A(A<float, 1>* a){ 
     //do something 
    } 
. 
. 
. 
}; 
+0

esto también se puede hacer con funciones de plantilla definiendo una clase que no sea de plantilla, un constructor de plantilla que se llame desde un constructor predeterminado que no sea de plantilla. – user2616927

0

Aquí hay una solución.

Haz una plantilla de la subclase B de A. Haz la parte independiente del argumento de la plantilla de la construcción en el constructor de A. Haga la parte dependiente de argumento de plantilla en el constructor de B.

12
template<class...>struct types{using type=types;}; 
template<class T>struct tag{using type=T;}; 
template<class Tag>using type_t=typename Tag::type; 

los ayudantes anteriores le permiten trabajar con tipos como valores.

class A { 
    template<class T> 
    A(tag<T>); 
}; 

el tipo tag<T> es una variable con ningún estado además del tipo que la caries. Usted puede usar esto para pasar un valor de tipo puro en una función de plantilla y tiene el tipo deducirse por la función de plantilla:

auto a = A(tag<int>{}); 

se puede pasar en más de un tipo:

class A { 
    template<class T, class U, class V> 
    A(types<T,U,V>); 
}; 
auto a = A(types<int,double,std::string>{}); 
1

usted podría hacer esto:

class C 
{ 
public: 
    template <typename T> C(T*); 
}; 
template <typename T> T* UseType() 
{ 
    static_cast<T*>(nullptr); 
} 

continuación para crear un objeto de tipo C usando int como el parámetro de plantilla para el constructor:

C obj(UseType<int>()); 

Dado que no puede pasar parámetros de plantilla a un constructor, esta solución básicamente convierte el parámetro de plantilla en un parámetro regular. El uso de la función UseType<T>() al llamar al constructor le deja claro a quien mira el código que el propósito de ese parámetro es decirle al constructor qué tipo usar.

Un caso de uso para esto sería si el constructor crea un objeto de clase derivado y lo asigna a una variable miembro que es un puntero de clase base. (El constructor necesita saber qué clase derivada usar, pero la clase en sí misma no necesita ser modelada ya que siempre se usa el mismo tipo de puntero de clase base.)