2011-03-10 11 views
5

¿Hay alguna manera de usar el Named Constructor Idiom con plantillas de forma "bonita"?Named Constructor Idiom and Templates?

Por ejemplo:

#include <vector> 
using namespace std; 

template< typename T > 
class Foo 
{ 
public: 
    static Foo Copy(const T& arg) 
    { 
     Foo ret; 
     ret.t_copy = arg; 
     return ret; 
    } 

    static Foo CopyClear(const T& arg) 
    { 
     Foo ret; 
     ret.t_copy = arg; 
     ret.t_copy.clear(); 
     return ret; 
    } 

private: 
    T t_copy; 
}; 


int main(int argc, char** argv) 
{ 
    vector<double> vec; 
    vec.push_back(1); 

    // #1: won't compile 
    Foo< vector<double> > a_foo = Foo::CopyClear(vec); 

    // #2: ugly, but works 
    Foo< vector<double> > a_foo = Foo< vector<double> >::CopyClear(vec); 

    return 0; 
} 

Me gustaría utilizar la sintaxis de #1 de alguna manera. #2 funciona pero frota mi sentido DRY de la manera incorrecta.

EDITAR: Nueva versión más "realista" de Foo.

Edit2: No C++ 0x/C++ 1x para mí Me temo :(

+0

donde se # 2 o ¿se refiere a la _rvalue_ scoped correctamente llamada que no es ambiguo? ¿Consideras una solución más elegante como pasar el tamaño al constructor? – AJG85

+0

En los comentarios marqué la segunda invocación de 'CopyClear()' como # 2. – genpfault

Respuesta

3

respuesta Actualizado

Si entiendo su intención correcta, esto va a hacer el truco:

template< typename T > 
class Foo 
{ 
private: 
    friend class FooHelper; 
    size_t sz; 
}; 

class FooHelper 
{ 
public: 
    template< typename T > 
    static Foo<T> Size(const T& arg) 
    { 
     Foo<T> ret; 
     ret.sz = arg.size(); 
     return ret; 
    } 

    template< typename T > 
    static Foo<T> HalfSize(const T& arg) 
    { 
     Foo<T> ret; 
     ret.sz = arg.size()/2; 
     return ret; 
    } 
}; 

Este compila:

int main(int argc, char** argv) 
{ 
    vector<double> vec; 
    vec.push_back(1); 

    Foo<vector<double>> a_foo = FooHelper::HalfSize(vec); 
} 
+0

Válido para el ejemplo dado, sin embargo, supongo que OP quiere usar T en su clase, en lugar de solo los constructores. Además, no estoy al tanto de ninguna solución a la pregunta real, por lo que podría no ser posible de la forma en que lo desea (puedo estar equivocado). – dialer

+0

'Foo' también está modelado. – genpfault

+0

@dialer, @genpfault: en ese caso, puede crear una 'plantilla class Bar' y crear una instancia de los objetos que se devolverán a partir de los métodos estáticos' Foo'. En efecto, esto traería de vuelta el problema de repetir el tipo de argumento de la plantilla, pero en este caso sería la repetición de 'T' dentro de' Foo' (que se puede considerar como "código de la biblioteca"). De todos modos, si el OP proporciona más información, podemos adaptar nuestras respuestas. – Jon

3

no lo hago piense que hay un problema SECO, piense en ello como una restricción de idioma. Si usted tiene una clase Foo sin plantilla, pero desea crear un nuevo objeto a partir de un método estático, que tendría que hacer algo como:

Foo a_foo = Foo::HalfSize(something); 

y no hay, por supuesto, la Foo repitió dos veces.

Así que, como aquí el nombre completo de la clase es Foo< vector<double> >, es lógico obtener el método estático de Foo< vector<double> >::HalfSize(), ya que esa es la forma C++.

3

Además de la respuesta de @ Jon, consulte std::make_pair y su relación con std::pair, si necesita que la clase sea una plantilla.

+1

Y también, 'make_shared' y' shared_ptr'. Considero un modismo el uso de funciones de plantilla para crear objetos de plantilla, para beneficiarse de la deducción de tipo. –

2

Si puede usar las características de C++ 0x, la palabra clave auto sería útil. ¿Hay alguna razón por la cual Size() y HalfSize() tengan que ser métodos estáticos? Si usted proporciona métodos para mutar sz usted puede hacer esto:

template<class T> 
Foo<T> HalfSize(const T& arg) 
{ 
    Foo<T> ret; 
    ret.setSz(arg.size()/2); // or similar 
    return ret; 
} 

y luego # 1 es un poco más alcanzable.

+0

No C++ 0x para mí Tengo miedo :( – genpfault

2

C++ 1x al rescate:

auto a_foo = Foo::HalfSize<vector<double>>(vec); 

Y, sí, los dos >> cierre se analiza como > > en C++ 1x.

Probablemente ya esté disponible con un compilador cerca de usted.

+0

No C++ 1x para mí Tengo miedo :( – genpfault

+0

@genpfault: ¿Qué compilador estás usando? – sbi

+0

Visual C++ 2008. – genpfault

3

Esto es técnicamente bien y es posiblemente la respuesta más simple a su fin pregunta:

Foo< vector<double> > a_foo = a_foo.CopyClear(vec); 

Es técnicamente bien porque CopyClear es una función static miembro.

Y no hay ningún problema técnico, p. podría usar un typedef en su lugar. O simplemente ponga esas funciones de miembro static en el ámbito del espacio de nombres, como plantillas de función. O en alguna clase de ayuda, como alguien ya ha sugerido.

Pero a pesar de que no hay ningún problema técnico, el diseño es menos que ideal; es, para decirlo sin rodeos (lo siento), un poco peor que insignificante.

Por ejemplo, en CopyClear, ¿por qué está copiando un vector y luego descartando el resultado de la copia? Solo necesita crear un vector vacío, del tipo que su código conoce.

Y, por ejemplo, ¿por qué está introduciendo maquinaria de efectos secundarios?

Deben evitarse y eliminarse los efectos secundarios, no introducidos.

Saludos & HTH.,