2011-03-28 19 views
6

I tiene una clase A:conversión implícita a un objeto de C++ plantilla

template <typename T, int I> struct A {}; 

y una B. clase me gustaría objeto de de tipo B para convertir implícitamente a A cuando se administra como argumentos de la función. B es el siguiente:

template <typename T> 
struct B { 
    operator A<T,0> &() const { return *new A<T,0>(); } 
}; 

Sin embargo, mi prueba (continuación) falla con GCC 4.5, dando el error: función ninguna coincidencia para la llamada a 'prueba (B &)' dónde voy mal aquí? ¿Otros compiladores también rechazan esto?

template <typename T, int I> 
void test(A<T,I> &a) { delete &a; } 

int main(int argc, char *argv[]) 
{ 
    B<int> b; 
    test(b); 
    return 0; 
} 

p.s. Ahora he puesto mi propia solución en una respuesta a continuación.

+0

quiero señalar la pérdida de memoria, pero parece como si esto era poner juntos para mostrar el tema, así que no lo hará. Voy a votar mejor en su lugar. :-) – James

+0

clase? -------- –

+0

Gracias James :) Reparado. – user2023370

Respuesta

2

Si desea una conversión implícita de B a A que se necesita, ya sea:

Un operador de conversión de B:

operator A<T,0>(); 

o un Un constructor que toma una referencia B:

A(const B& other); 

o para B derivar de A. Lo que usted ha declarado:

operator A<T,0> &() const; 

se parece un poco a una dirección de sobrecarga mal declarada.

Sin embargo, dado que test() toma una referencia (y no const), la opción del operador de casting no funcionará.

Esto es lo que he probado:

template <typename T, int I> struct A {}; 

template <typename T> 
struct B { 
    //operator A<T,0> &() const { return *new A<T,0>(); } 

    template< int I > 
    operator A<T, I>() const { return A< T, 0 >(); } 
}; 

template <typename T, int I> 
void test(A<T,I> &) { } 

int f() 
{ 
    B<int> b; 

    A<int, 0> a(b); 
    test(a); // <-- Success 

    test(b); // <-- Failure, "could not deduce template argument" 
    return 0; 
} 

conversión a un inicializando una variable local funciona bien.

+0

Parafraseando de una manera manual: como test() toma una referencia no constante a una A, necesita pasar algo con el tipo A que puede cambiar. Como B no se deriva de A, nada de lo que puede declarar en B le permitirá hacerlo implícitamente. – RobH

+0

Nunca lo he hecho, pero creo que es posible declarar un operador de conversión que devuelve 'A <...> &' si se usa typedef. Por supuesto, para que esto sea útil, B necesita tener una instancia no constante de A en ella. – Arkadiy

3

No relacionado con su problema, pero return *new A<T,0>(); es incorrecto ya que pierde memoria invita a una pérdida de memoria. Debería no usar new aquí. return A<T, 0>(); y eliminar la referencia del tipo de devolución funciona bien y no pierde memoria.

+0

Gracias Konrad. Creo que eso me obligaría a hacer que la prueba acepte solo argumentos de referencia de referencia. – user2023370

+0

'test' toma su parámetro por referencia no constante, lo que es aún más confuso (si quiere modificar su parámetro). –

+0

@ user643722 Es cierto. Sin embargo, podrías sobrecargarlo. –

1

¿Estás seguro de que realmente quieres una conversión implícita aquí? Parece una manera perfecta de confundirse a usted mismo u otro mantenedor, o peor, llamar a una función utilizando el argumento equivocado porque la conversión implícita lo permite. En cambio, considere una plantilla make_A como std::make_pair para mostrar explícitamente su intención en el sitio de la llamada.

+0

Gracias Mark. El código se genera automáticamente y, una vez que se encuentra una solución, no será modificado por los usuarios finales. – user2023370

+0

@ user643722 Entonces esa es una razón más para usar la llamada explícita ya que se genera automáticamente. Es de suponer que los humanos todavía tienen que leer el código. –

0

Esto también falla en VC++. Para que funcione, añadir este método:

template <typename T> 
void test(B<T> &b) 
{ 
    test(static_cast< A<T,0>& > (b)); 
} 

Esto tomará una real B, hacer explícita la conversión (a través de static_cast), y luego llamar a test utilizando el A que acaba de hacer.

Obtengo errores cuando lo ejecuto sin embargo. Espero que esto sea solo un código de ejemplo, y no estás haciendo delete &a en tu código actual. RobH le dio un mejor operador de conversión y evita el desorden del puntero.

+0

Probablemente debería convertirlo a una referencia: 'static_cast &> (b)', de lo contrario, se realiza una copia cuya prueba intenta eliminar. – UncleBens

+0

D'oh! Sí, eso soluciona mi problema. Buena captura, @UncleBens – Tim

1

Proporcionar una sobrecarga de test como la siguiente permitirá escribir test(b) como en la pregunta.
Por ejemplo:

template <typename T> 
void test(B<T> const &b) { 
    test(static_cast< A<T,0>& >(b)); 
} 

test(b); // caller 

Si dicha sobrecarga no está permitido, pero se le permite modificar B 's definición, ¿qué hay de proporcionar la función miembro que devuelve A como la siguiente, en lugar de la función de conversión?

template <typename T> 
struct B { 
    A<T,0> &to_A() const { return *new A<T,0>(); } 
}; 

test(b.to_A()); 

Si no se le permite modificar la definición B 's, lo anterior to_A habrá una función libre como el siguiente en lugar de la función miembro:

template <typename T> 
A<T,0> &to_A(B<T> const &b) { 
    return static_cast< A<T,0>& >(b); 
} 

test(to_A(b)); 

Esperanza esto ayuda

0

Me he decidido a usar el operador de conversión de paso por valor sugerido por Konrad; la llamada a la función de plantilla explícita de Let_Me_Be; y rocié un poco de magia C++ 0x en la parte superior: una referencia de valor en el parámetro para probar. (O, si fuera C++ 2011 ahora?)

template <typename T, int I> struct A { int x; }; 

template <typename T> 
struct B { 
    operator A<T,0>() const { return A<T,0>(); } 
}; 

template <typename T, int I> 
void test(A<T,I> &&a) { a.x=7; printf("%d\n", x); } 

int main(int argc, char *argv[]) 
{ 
    B<int> b; 
    test<int,0>(b); 
    return 0; 
} 
Cuestiones relacionadas