2010-02-21 21 views
5

Esta pregunta está relacionada con mi última one. Estoy tratando de resolver el problema usando traits<T> y traits<T*>. Por favor considere el siguiente código.Usar rasgos en C++

template<typename T> 
struct traits 
{ 
    typedef const T& const_reference; 
}; 

template<typename T> 
struct traits<T*> 
{ 
    typedef const T const_reference; 
}; 

template<typename T> 
class test 
{ 
public: 
    typedef typename traits<T>::const_reference const_reference; 
    test() {} 
    const_reference value() const { 
     return f; 
    } 
private: 
    T f; 
}; 

int main() 
{ 
    const test<foo*> t; 
    const foo* f = t.value(); // error here. cannot convert ‘const foo’ to ‘const foo*’ in initialization 
    return 0; 
} 

lo que parece que el compilador no está considerando la especialización rasgos para los punteros y tomando tipo de retorno de value() como const foo en lugar de const foo*. ¿Qué estoy haciendo mal aquí?

¡Cualquier ayuda sería genial!

Respuesta

2

La especialización se está utilizando. traits<foo*>::const_referenceesconst foo. Si usted quiere que sea un puntero, utilice:

template<typename T> 
struct traits<T*> 
{ 
    typedef const T* const_reference; 
}; 

Con esto, habrá traits<foo*>::const_referenceconst foo*.

Tenga en cuenta que el uso de T en la especialización traits<T*> está completamente separado de la T en la plantilla traits. Podría cambiarle el nombre:

template<typename U> 
struct traits<U*> 
{ 
    typedef const U* const_reference; 
}; 

y tendrá la misma especialización. Tiene más sentido si tienes experiencia en programación funcional.

Para empezar, piense en el template <typename ...> como la introducción de una abstracción, más bien como una función que abstrae un valor. Es como si pasara

sum = 0 
for item in [1,2,3]: 
    sum += item 

en:

function sum(l): 
    sum = 0 
    for item in l: 
     sum += item 
    return sum 

donde l toma el lugar de [1,2,3]. Podemos llamar sums de otra función que en sí tiene un parámetro formal llamado l:

function sumsq(l): 
    return sum(map(lambda x: x*x, l)) 

sumsq 's "l" no tiene nada que ver con sum' s "l".

Con las plantillas, se abstraen los nombres de los tipos en lugar de los valores. Es decir, convertir:

struct traits { 
    typedef const double& const_reference; 
}; 

en:

template <typename T> 
struct traits { 
    typedef const T& const_reference; 
}; 

Consideremos ahora una especialización no molde:

template <> 
struct traits<double*> { 
    typedef const double* const_reference; 
}; 

Aquí no hay parámetros de plantilla para la especialización, pero se puede piense en traits<double*> como aplicar una plantilla traits a double*. Resumen de la double y tiene:

template <typename T> 
struct traits<T*> { 
    typedef const T* const_reference; 
}; 

Aquí el T es un parámetro para la especialización, no es la plantilla base.

+0

Me siento volcado :(Pero, T en sí es un puntero ('foo *'). Así que especificar 'T *' dará lugar a 'T **'? –

+0

No. En los 'rasgos ' especialización, 'T 'no es un puntero,' T * 'es un puntero. Que use el mismo parámetro typename es inconsecuente. – outis

+0

OK. Gracias. Tiene sentido ahora .. –