Tengo una plantilla class A

template <unsigned int m>
class A
{
public:
    A(int) {}
};

Que tiene un constructor de int. Y tengo una operación:

template<unsigned int m>
A<m> operator+(const A<m>&, const A<m>&)
{
    return A<m>(0);
}

Pero cuando me llaman:

A<3> a(4);
A<3> b = a + 5;
A<3> c = 5 + a;

Me gustaría int convertir implícitamente a Una, pero los compiladores tira error.

Es allí cualquier manera elegante para habilitar la conversión implícita sin el uso de soluciones como:

  • a + A<m>(5)
  • operator+<3>(a, 5)
  • La única manera de conseguir que esto funcione es proporcionar una sobrecarga (de operator+) que acepta un int.
  • Aquí es una buena respuesta a un pedido previamente pregunta: stackoverflow.com/a/2833761/1257977
  • En a + 5, lo que debe 5 convertirse en un A<5>(5) o un A<3>(5)? Si el último, donde debe el 3 vienen?
  • Se debe convertir a A<3>(5). El m valor en + es el mismo para los dos argumentos, por lo que se debe tomar desde el primer argumento.
  • Reacción instintiva: el uso de explicit en el de constructores, por favor. Las conversiones implícitas debe ser reservada para casos muy especiales…
  • Pero este es un caso raro, cuando debería uso implícito constructor.

InformationsquelleAutor Seagull | 2012-03-20

4 Comentarios

  1. 37

    Que la solución ya está demostrado en esta respuesta. Ahora, más sobre el problema…

    El problema en tu código es cómo la resolución de sobrecarga se realiza. Cuando una plantilla de función es considerado para la resolución de sobrecarga que el compilador de realizar el tipo de deducción en los argumentos y llegar a un tipo de sustitución que coincide con la llamada o de lo contrario no aplicar esa plantilla, se quita el conjunto de los posibles candidatos y continúa más. El problema en este punto es que el tipo de deducción sólo se deduce coincidencias exactas (con posibles extra const/volátiles de la calificación). Debido a la coincidencia es exacta, el compilador no utilizar ningún tipo de conversión (de nuevo, distinto cv).

    El ejemplo más simple de esto sucede con std::max y std::min funciones:

    unsigned int i = 0;
    std::min( i, 10 );    //Error! 

    Tipo de deducción será deducir la T en template <typename T> min( T const &, T const & ) a ser unsigned para el primer argumento pero int para el segundo se diferencian y que el compilador de descartar esta función de la plantilla.

    La solución propuesta en el respuesta es el uso de una característica del lenguaje que permite definir un amigo función dentro de la definición de la clase. La ventaja de las plantillas es que para cada (diferentes) de la creación de instancias de la plantilla, el compilador creará un libre no-función de la plantilla a nivel de espacio de nombres que tiene la firma obtenidos mediante la sustitución de los tipos reales de la creación de instancias en el amigo de la declaración:

    template <typename T>
    class test {
        friend test operator+( test const & lhs, test const & rhs ) {  //[1]
            return test();
        }
    }
    test<int> t;                                                       //[2]

    En el ejemplo anterior, el compilador le permite añadir la definición de la función friend dentro del ámbito de la clase en [1]. Luego, cuando se crea la instancia de la plantilla en [2], el compilador generará un libre función:

    test<int> operator+( test<int> const & lhs, test<int> const & rhs ) { 
       return test<int>();
    }

    La función está definida siempre, ya sea que utilice o no (esto es diferente a la plantilla de funciones miembro de clase, que se crea una instancia de la demanda).

    La magia aquí tiene múltiples facetas. La primera parte es que genéricamente se están definiendo los no-plantilla de funciones para cada una de las instancias de los tipos, por lo que la ganancia genericity y al mismo tiempo la ventaja de la resolución de sobrecarga ser capaz de utilizar esta función cuando los argumentos no son perfectos partidos.

    Porque no es una plantilla de función, el compilador es capaz de llamar a las conversiones implícitas en ambos argumentos, y usted recibirá su comportamiento esperado.

    Además, un tipo diferente de magia continúa con la búsqueda, como la función definida de este modo sólo puede ser encontrado por el argumento de la búsqueda dependiente de menos también es declarada en el nivel de espacio de nombres, que en nuestro caso no se puede hacer de una manera genérica. La implicación de esto puede ser bueno o malo, dependiendo de cómo usted quiere que lo consideren…

    Debido a que solo puede ser encontrado por ADL no será considerado, a menos que al menos uno de los argumentos que ya es del tipo deseado (es decir, nunca será utilizado para realizar conversiones a tanto argumentos). La desventaja es que es imposible referirse a la función a menos que realmente llamar, y eso significa que usted no puede obtener un puntero a función.

    (Más información en la plantilla de la amistad aquí, pero tenga en cuenta que en este caso en particular, todas las otras variantes no podrá realizar las conversiones implícitas).

    • Es allí una manera de definir esta sobrecarga de operadores fuera de la clase y sólo tiene un «especial» amigo de la declaración en el cuerpo de la clase? No he encontrado la manera de hacerlo.
    • No entiendo muy bien lo que están pidiendo
    • Por lo general prefieren la separación de declaración y de ejecución. Así que, si quiero poner sólo el amigo de la declaración de la clase, pero tiene la definición de la función en otros lugares, hay una manera de hacer esto?
    • Tipo de finales, pero ni siquiera 3 años… no Se puede definir que un amigo de la función externamente como una función diferente para cada instancia de la plantilla. Lo que usted puede hacer para evitar la adición de la cantidad de código que hay que proporcionar una función que hace el trabajo real (es decir static test add(test const&, test const&)) en la clase y tener la definición de la amiga acaba de presentar: test operator+(test const& a, test const& b) { return add(a,b); }. No es perfecto, pero al menos la definición de la clase es de una sola línea. La lógica más compleja que está oculto en test<T>::add.
    • Esta respuesta no es correcta. Se dice que la coincidencia debe ser exacta «con posibles extra const/volátiles de la calificación». Sin embargo, derivado a la base, así como ciertos puntero de calificación de las conversiones están permitidos (por ejemplo, int * a int const *). en.cppreference.com/w/cpp/language/template_argument_deduction. Feliz editar si usted está de acuerdo.
  2. 18

    Cada intento de proporcionar un operador mediante el uso de plantillas necesitará por lo menos un segundo de sobrecarga. Pero usted puede evitar que por la definición del operador dentro de la clase:

    template <unsigned int m>
    class A
    {
    public:
      A(int) {}
      inline friend A operator+(const A& a, const A& b) { return A(0); }
    };

    Funciona para ambos, a+5 y 5+a.

    • +1, Un poco más de explicación podría ser apropiado para aquellos que no saben la razón.
    • Pero necesito plantilla A<m>, como me gustaría forbide operación para objetos con diferentes m.
    • Debe ser también inline
    • El A sin parámetros de la plantilla dentro de la definición de la clase de plantilla A se refiere a A<m> no sólo alguna A<x>.
    • He proporcionado una mucho más larga explicación de lo que realmente está pasando y por qué es la solución de una respuesta.
  3. 1

    Agregar este operador

    template<unsigned int m>
    A<m> operator+(const A<m>&, const int&)
    {
        return A<m>(0);
    }

    O intente este

    template <unsigned int m>
    class A
    {
    friend const A operator+(const A& a, const A& b) { return A(0); }
    public:
        A(int) {}
    //OR FOR UNARY
        //const A operator+(const A &a) const {return A(0);}
    };
    
    
    int main(){
        A<3> a(4);
        A<3> b = a + 5;
        A<3> c = 5 + a;

    }

    • No es una solución elegante como también debo agregar A<m> operator+( const int&, const A<m>&) y hacerlo para todas las demás operaciones.
    • definir «elegante» por favor
    • el reloj de mi, además de que la respuesta no sé es elegante, aunque :).
    • pero funciona con su ejemplo
    • Usted también necesitará una sobrecarga para 5 + a.
    • Esto no está en la pregunta.
    • Gracias a esta solución es mucho mejor, pero algo que debe ser creado para 5+a
    • especificar en su pregunta, por favor
    • Es bastante común que un operador binario que ser simétrica (y por lo general se esperaba), así que mientras no se especifica en la pregunta, sería cortés para proporcionar la respuesta.
    • buscar otra variante

  4. 0

    Podría intentar agregar un adicional de «política» tipo de argumento a la plantilla para su A de la clase que va a determinar el tipo de conversión que desee. Por ejemplo:

    template <unsigned int m, typename ConvVal = int>
    class A
    {
            public:
                    typedef ConvVal conv_val;
    
                    A(ConvVal) {}
    };
    
    template<template <unsigned int, class U> class T, unsigned int m, typename U>
    T<m, U> operator+(const T<m, U>&, const T<m, U>&)
    {
            return T<m, U>(0);
    }
    
    template<template <unsigned int, class U> class T, unsigned int m, typename U>
    T<m, U> operator+(const T<m, U>&, const typename T<m, U>::conv_val&)
    {
            return T<m, U>(0);
    }
    
    template<template <unsigned int, class U> class T, unsigned int m, typename U>
    T<m, U> operator+(const typename T<m, U>::conv_val&, const T<m, U>&)
    {
            return T<m, U>(0);
    }
    
    int main()
    {
            A<3> a(4);
            A<3> b = a + 5;
    
            return 0;
    }

    Ahora su A clase tendrá una plantilla adicional argumento de que los valores predeterminados para un int tipo, y se define el tipo real que permitirá la conversión automática de. Usted sólo necesita a una sobrecarga del operator+ función de tres veces, una vez para la versión sin el valor de conversión, que tendrá explícita clases de tipo A<m, T>, y otro para las dos versiones de operator+ que se llevará a conversión de tipos. En el código anterior he generalizado de esto con más tipos genéricos, de manera que esto se puede hacer con casi cualquier otra clase que tiene la debida firma de la plantilla, y se define un conv_val typedef.

Dejar respuesta

Please enter your comment!
Please enter your name here