Considerar tengo el siguiente código mínimo:

#include <boost/type_traits.hpp>

template<typename ptr_t>
struct TData
{
    typedef typename boost::remove_extent<ptr_t>::type value_type;
    ptr_t data;

    value_type & operator [] ( size_t id ) { return data[id]; }
    operator ptr_t & () { return data; }
};

int main( int argc, char ** argv )
{
    TData<float[100][100]> t;   
    t[1][1] = 5;
    return 0;
}

GNU C++ me da el error de:

test.cpp: In function 'int main(int, char**)':
test.cpp:16: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for second:
test.cpp:9: note: candidate 1: typename boost::remove_extent<ptr_t>::type& TData<ptr_t>::operator[](size_t) [with ptr_t = float [100][100]]
test.cpp:16: note: candidate 2: operator[](float (*)[100], int) <built-in>

Mis preguntas son:

  1. Por qué GNU C++ da el error, pero Intel C++ compiler no?
  2. ¿Por qué el cambio de operator[] a los siguientes conduce a compilar sin errores?

    value_type & operator [] ( int id ) { return data[id]; }

Enlaces para el Estándar de C++ son apreciados.


Como puedo ver que aquí hay dos rutas de conversión:

  1. (1)int a size_t y (2)operator[](size_t).
  2. (1)operator ptr_t&(), (2)int a size_t y (3)construir-en operator[](size_t).
  • que compilador? gcc 4.1 y 4.3 no da error
  • g++ 4.2.4 (y también MSVC++2008/2010). Véase también: codepad.org/54uGSrFS
  • Esto puede estar relacionado con cómo size_t se define en cierta aplicación. Por ejemplo, MSVC++ compilador da el error sólo en x32, pero no en x64. Esto nos llevaría a mi segunda pregunta.
  • Eres el tratamiento de las advertencias de los errores? – Creo que la ambigüedad proviene de la llanura de las matrices de ser indexado con int (en este caso), y que, de hecho, llamando operator[] con argumentos de tipo int, así que algo debe estar implícitamente a algo para que esto funcione.
  • Eso es porque, entonces, no entre en conflicto con la incorporada en el candidato. Definido por el usuario de la conversión de la secuencia de U1 es una mejor conversión de la secuencia de otro definido por el usuario de la conversión de la secuencia de U2 si contienen la misma conversión definida por el usuario o de la función de constructor y si el segundo estándar de conversión de la secuencia de U1 es mejor que el segundo estándar de conversión de la secuencia de U2.
  • UncleBens, Esto es un error, no una advertencia.
  • C++0x introduce el uso de la explicit palabra clave para los operadores de conversión. Tengo que admitir que prefiero NO usarlos mientras tanto.
  • Kirill, se dice que es un error con el -Werror bandera, sin la bandera que dice que esto es una advertencia. Por eso he preguntado.
  • g++ 4.2.4 da el error que he publicado sin -Werror bandera.
  • Un buen ejemplo, que los operadores de conversión son malos.
  • Este es un duplicado. Aquí alguien tenía algún problema: stackoverflow.com/questions/384200/c-operator-ambiguity y aquí, stackoverflow.com/questions/1726740/… y aquí lo tengo en una respuesta demasiado: stackoverflow.com/questions/1307876/… (desplácese hacia abajo para «la Conversión de los no-tipos de clase»).
  • buen ejemplo, que los operadores de conversión son malas». No, No es así.

6 Comentarios

  1. 31

    Es en realidad bastante sencillo. Para t[1], la resolución de sobrecarga tiene estos candidatos:

    Candidato 1 (builtin: 13.6/13) (T ser arbitraria en el tipo de objeto):

    • Lista de parámetros: (T*, ptrdiff_t)

    Candidate 2 (el operador)

    • Lista de parámetros: (TData<float[100][100]>&, something unsigned)

    La lista de argumentos es dada por 13.3.1.2/6:

    El conjunto de candidatos para las funciones de resolución de sobrecarga es la unión de los estados candidatos, los no-miembros de los candidatos, y el construido-en los candidatos. El argumento de la lista contiene todos los operandos del operador.

    • Lista de argumentos: (TData<float[100][100]>, int)

    Puede ver que el primer argumento coincide con el primer parámetro del Candidato 2 exactamente. Pero necesita un usuario de conversión definidos para el primer parámetro del Candidato 1. Así que para el primer parámetro, el segundo candidato que gana.

    También se puede ver que el resultado de la segunda posición depende. Vamos a hacer algunas suposiciones y ver lo que tenemos:

    1. ptrdiff_t es int: El primer candidato gana, porque tiene una coincidencia exacta, mientras que el segundo candidato requiere de un integrante de la conversión.
    2. ptrdiff_t es long: Ninguno de los candidatos gana, porque ambos requieren de un integrante de la conversión.

    Ahora, 13.3.3/1 dice

    Vamos a ICSi(F) denota la conversión implícita de la secuencia que convierte el i-ésimo argumento en la lista el tipo del i-ésimo parámetro de viable función F.

    Viable de función F1 se define como una función mejor que otro viable función F2 si para todos los argumentos que yo, ICSi(F1) no es peor conversión de la secuencia de ICSi(F2), y luego … para algunos el argumento j, ICSj(F1) es una mejor conversión de la secuencia de ICSj(F2), o, si no que …

    Para nuestra primera suposición, no conseguimos un ganador, porque el Candidato 2 victorias para el primer parámetro, y el Candidato 1 gana para el segundo parámetro. Yo lo llamo el criss-cross. Para nuestra segunda hipótesis, la Candidata ganadora de 2 carreras en general, porque ninguno de los parámetros tenido un peor conversión, pero el primer parámetro había un mejor de conversión.

    Para el primer supuesto, no importa que la integral de conversión (unsigned int a) en el segundo parámetro es menos malo que el usuario define la conversión de los otros candidatos en el primer parámetro. En la cruz de criss, las reglas están crudo.


    Este último punto aún podría confundir, porque de todo el revuelo alrededor, así que vamos a hacer un ejemplo

    void f(int, int) { }
    void f(long, char) { }
    
    int main() { f(0, 'a'); }

    Esto le da la misma confuso GCC advertencia (que, recuerdo, era realmente confuso el infierno fuera de mí cuando lo he recibido hace algunos años), porque 0 convierte a long peor que 'a' a int – sin embargo, usted consigue una ambigüedad, porque están en una cruz de criss situación.

    • He una duda: ¿cómo se decide que conversar es peor? int a long es peor que char a int, ¿por qué no al revés ?
    • porque está definido por thenspec de esa manera. Vea integral de las promociones y/versus integral de las conversiones.
  2. 13

    Con la expresión:

    t[1][1] = 5;

    El compilador debe centrarse en el lado izquierdo para determinar lo que pasa allí, así que la = 5; es ignorada hasta el lhs se ha resuelto. Lo que nos deja con la expresión: t[1][1], que representa a dos operaciones, con el segundo operativo en el resultado de la primera, por lo que el compilador sólo debe tomar en cuenta la primera parte de la expresión: t[1].El tipo real es (TData&)[(int)]

    La llamada no coincide exactamente con ninguna de las funciones, como operator[] para TData se define como tomar un size_t argumento, así que para ser capaz de utilizar el compilador tendría que convertir 1 de int a size_t con una conversión implícita. Que es la primera opción. Ahora, otra ruta posible es la aplicación definida por el usuario de conversión para convertir TData<float[100][100]> en float[100][100].

    La int a size_t la conversión es un integral de conversión y es clasificado como Conversión en la Tabla 9 de la norma, como es el definido por el usuario de conversión de TData<float[100][100]> a float[100][100] de la conversión de acuerdo a §13.3.3.1.2/4. La conversión de float [100][100]& a float (*)[100] está clasificado como Coincidencia Exacta en la Tabla 9. El compilador no está permitido a elegir entre esos dos de conversión de secuencias.

    Q1: No todos los compiladores se adhieren a la norma de la misma manera. Es muy común encontrar que en algunos casos específicos, un compilador puede realizar de forma diferente que los demás. En este caso, el g++ ejecutores decidió quejarse acerca de la norma de no permitir que el compilador para elegir, mientras que el Intel ejecutores, probablemente, sólo silencio aplicado a su preferido conversión.

    Q2: Cuando se cambia la firma del usuario definido operator[], el argumento coincide exactamente con el pasado en el tipo. t[1] es un partido perfecto para t.operator[](1) sin conversiones de ningún tipo, por lo que el compilador debe seguir ese camino.

    • +1: Muy buena explicación.
    • Creo que se puede hacer de esta la respuesta más simple en el segundo párrafo. Usted no tiene que mirar en la segunda [] a todos. Sólo se necesita considerar cómo t[1] se interpreta. Tipo de tipos de TData (lvalue) y int (r-value).
    • Me di cuenta de que el segundo par de [] no tuvo en cuenta, ya que de ello depende el resultado de la primera.
    • La tabla 9 es sólo para el Estándar de Conversión de secuencias – «Cada conversión en la Tabla 9 también tiene asociado un rango (Coincidencia Exacta, de Promoción, o de Conversión). Estos se utilizan para clasificar estándar de conversión de secuencias (13.3.3.2).». Así funciona la lógica de mantener realmente bueno?
    • 13.3.3.1.2/4 hace referencia a la clase base de las conversiones. float[100][100] no es una clase base de TData<> por lo que es un arenque rojo.
    • También, @chubsdad es correcta: la función de conversión es una conversión definida por el usuario, mientras que int a size_t es un estándar de conversión, que es preferible según 13.3.3.1/3.
    • Sin embargo, a pesar de ir a través de todo este trabajo para demostrar que la opción #1 es mejor que la opción #2, creo que eso no demuestra nada, ya que el compilador ya nos dijo que. El mensaje de error se refiere a «ISO C++», aparentemente lo que implica que hay algún fantasma de texto que deshace la importancia de esta clasificación.
    • Tener actualizado mi post acerca de la razón detrás de la ambigüedad
    • Yo estaría encantado si pudiera quitar mi downvote de esta respuesta. Pero usted tendría que corregir en primer lugar, incluso si solo llamativos todo eso y admitiendo que sus sospechas estaban equivocados. No es una «Muy buena explicación.», Lo siento.

  3. 0

    No sé cuál es la respuesta exacta, pero…

    Porque de este operador:

    operator ptr_t & () { return data; }

    existen ya construido-en [] operador (matriz de suscripción) que acepta size_t como índice. Así que tenemos dos [] operadores, la incorporada en el y definidas por el usuario. Stand acepta size_t así que esta es considerada como ilegal sobrecarga probablemente.

    //EDITAR

    esto debería funcionar como la intención de

    template<typename ptr_t>
    struct TData
    {
        ptr_t data;
        operator ptr_t & () { return data; }
    };
    • Su //EDIT no funciona en Intel C++.
  4. 0

    A mí me parece que con

    t[1][1] = 5;

    el compilador tiene para elegir entre.

    value_type & operator [] ( size_t id ) { return data[id]; }

    que haría coincidir si la int literal iban a ser convertidos a size_t, o

    operator ptr_t & () { return data; }

    seguido por la matriz normal de la indización, en cuyo caso el tipo de índice coincide exactamente.


    Como para el error, parece GCC como compilador de extensión gustaría elegir a la primera de sobrecarga para usted, y se compila con el -pedante y/o -Werror indicador de que las fuerzas que se pegue a la palabra de la norma.

    (No estoy en una pedante estado de ánimo, así que no hay citas de la norma, especialmente en este tema).

    • ¿Por qué en el segundo caso no hay int a size_t de conversión? ¿Qué tipo de normal array indexing es necesario? Yo pensaba que debería ser size_t demasiado.
    • V. Lyadvinsky: No, hay una construcción en operator[] para cada combinación de un tipo de puntero y de tipo entero. Un int funciona sin la necesidad de la conversión.
    • 13.6/13: «existen candidato del operador funciones de la forma … T& operator[](ptrdiff_t,T*);» Así que no, ptrdiff_t (no size_t) es especial. Ambos size_t y int requieren una conversión. (Lo que es más relevante, sin embargo, es la falta de una conversión en llamar a Kirill de la «revisión» de la sobrecarga.)
    • Acepto la corrección. Yo estaba buscando en la definición de operadores aditivos que no hace distinción entre los tipos enteros en «puntero plus entero» de los casos. Yo no sabía que la sección sobre resolución de sobrecarga hizo argumento especial.
    • Aunque int podría no requerir la conversión; tan lejos como puedo ver ptrdiff_t podría válidamente ser un typedef para int en algunas implementaciones.
    • es más probable que se long, que es distinta de int incluso si es el mismo tamaño.

  5. 0

    He tratado de mostrar los dos candidatos para la expresión t[1][1]. Ambos son de igual RANGO (de CONVERSIÓN). De ahí la ambigüedad

    Creo que el problema aquí es que la incorporada en el operador [] como por 13.6/13 se define como

    T& operator[](T*, ptrdiff_t);

    En mi sistema argumento se define como ‘int’ (¿ que explicar x64 comportamiento?)

    template<typename ptr_t> 
    struct TData 
    { 
        typedef typename boost::remove_extent<ptr_t>::type value_type; 
        ptr_t data; 
    
        value_type & operator [] ( size_t id ) { return data[id]; } 
        operator ptr_t & () { return data; } 
    }; 
    
    typedef float (&ATYPE) [100][100];
    
    int main( int argc, char ** argv ) 
    { 
        TData<float[100][100]> t;    
    
        t[size_t(1)][size_t(1)] = 5; //note the cast. This works now. No ambiguity as operator[] is preferred over built-in operator
    
        t[1][1] = 5;                 //error, as per the logic given below for Candidate 1 and Candidate 2
    
        //Candidate 1 (CONVERSION rank)
        //User defined conversion from 'TData' to float array
        (t.operator[](1))[1] = 5;
    
        //Candidate 2 (CONVERSION rank)
        //User defined conversion from 'TData' to ATYPE
        (t.operator ATYPE())[1][1] = 6;
    
        return 0; 
    }

    EDICIÓN:

    Aquí es lo que yo pienso:

    Para el candidato 1 (operador []) la conversión de la secuencia S1 es
    Definido por el usuario de conversión – Conversión Estándar (int a size_t)

    Para el candidato 2, la conversión de la secuencia S2 es
    Definido por el usuario de conversión -> int a argumento (para el primer argumento) -> int a argumento (por segundo argumento)

    La conversión de la secuencia S1 es un subconjunto de S2 y se supone para ser mejor. Pero aquí está la trampa…

    Aquí la continuación de la cita de la Norma deben ayudar.

    $13.3.3.2/3 estados – Estándar
    la conversión de la secuencia S1 es una mejor
    la conversión de la secuencia de estándar
    la conversión de la secuencia S2 si — S1 es un
    adecuado larga de S2 (comparar
    la conversión de secuencias en el
    la forma canónica definido por 13.3.3.1.1,
    con exclusión de cualquier Lvalue Transformación;
    la identidad de la conversión de la secuencia es
    considera una larga de cualquier
    la no-identidad de la conversión de la secuencia) o,
    si no que…

    $13.3.3.2 los estados- » definido por el Usuario
    la conversión de la secuencia de U1 es mejor
    la conversión de la secuencia de otro
    definido por el usuario de la conversión de la secuencia de U2 si
    ellos contienen la misma definidos por el usuario
    la función de conversión o constructor y
    si la segunda conversión estándar
    secuencia de U1 es mejor que el
    segundo estándar de conversión de la secuencia de
    U2.»

    Aquí la primera parte de la condición y el «si contienen la misma definidos por el usuario función de conversión o constructor» no se sostiene bien. Así que, incluso si la segunda parte de el y condición «si el segundo estándar de conversión de la secuencia de U1 es mejor que el segundo estándar de conversión de la secuencia de U2.» se mantiene bien, ni S1 ni S2 es preferido sobre los demás.

    Por eso gcc fantasma mensaje de error «ISO C++, dice que estos son ambiguos, incluso aunque lo peor de la conversión para que el primero es mejor que el peor de conversión para la segunda»

    Esto explica la ambigüedad tranquila bien en mi humilde opinión

    • No hay Convert argument 5 to size_t. Y lo User defined conversion quiso decir para Candidate 1?
    • La conversión de secuencias que usted ha proporcionado están equivocados.
    • ‘t’ está siendo convertida en un arreglo de punto flotante en el Candidato 1 usando el miembro de la clase de conversión del operador []. Me sale el punto 5 y 6 no llegar convertidos a size_t. Voy a cambiar de la publicación en breve
    • TData::operator[] no convertir t, que devuelve un subelemento de los estados data.
    • No es que la conversión por ejemplo, Un::operator int(){return m_data;}, decimos que convierte a ‘int’.
    • En realidad, no, operator type() son operadores de conversión y son cosas especiales, diferentes de [email protected] donde @ es un no-tipo. En general operador de conversión T1::operator T2 es una conversión, porque dada una variable v de tipo T1, puede ser utilizado en un contexto donde el tipo de T2 es necesario como en foo(v) con foo define como void foo(T2), de alguna manera, conversión, el elemento que coincida con T2. Tenga en cuenta que no hay ninguna llamada a la operadora. Por otro lado, operator[] es un método real de la llamada que el usuario debe realizar. No, no es diferente (además de la sintaxis de la llamada) de otros métodos.
    • ¿Puede dar un ejemplo de [email protected] donde @ es un no-tipo?
    • Cualquiera de operator+, operator-, operator[], operator()… Sólo aquellos que están operator type son operadores de conversión. La declaración o definición de la sintaxis difiere en que el tipo de retorno es implícitamente el tipo de operador, y ellos serán llamados en las mismas situaciones donde no definido por el usuario de las conversiones patada en (implícitamente, static_cast…)
    • Voy a conseguir. Gracias.
    • desde que te vi están muy interesados en la resolución de sobrecarga en muchos de tus posts, usted podría considerar la posibilidad de conseguir sus manos en «Plantillas de C++ – La Guía Completa». Contiene un apéndice acerca de la resolución de sobrecarga, y también contiene este mismo problema y lo explica.
    • Gracias. Permítanme polvo lejos el moho.

  6. 0

    La resolución de sobrecarga es un dolor de cabeza. Pero desde que te encontré en una revisión (eliminar la conversión del índice operando operator[]) que es demasiado específico para el ejemplo (literales son de tipo int pero la mayoría de las variables que va a utilizar no son), tal vez usted puede generalizar que:

    template< typename IT>
    typename boost::enable_if< typename boost::is_integral< IT >::type, value_type & >::type
    operator [] ( IT id ) { return data[id]; }

    Lamentablemente yo no puedo probar esta porque GCC 4.2.1 y 4.5 aceptar su ejemplo sin queja bajo --pedantic. Lo que realmente plantea la cuestión de si es un bug del compilador o no.

    También, una vez que he eliminado el Impulso de la dependencia, pasó Comeau.

    • En lugar de int o size_t, él puede usar ptrdiff_t y, a continuación, se trabajará con cualquier Estándar conforme compilador.
    • El punto es que sin la conversión… te importa para opinar sobre el tema?
    • así, las conversiones no le hará daño. Si utiliza ptrdiff_t, entonces cualquier argumento que el usuario pasa, en un conformes compilador que nunca van a llegar a ambigüedades, porque el grupo builtin operador utiliza ptrdiff_t demasiado (índice puede ser negativo). Esta es la razón por la que tienden a usar ptrdiff_t en mis clases el índice de los operadores.
    • Está usted seguro de que el mensaje de error Kirill consiguió que se refiere a múltiples estándar de las rutas de conversión? De todos modos, yo realmente no pretendo entender las reglas. Yo estoy trabajando a partir de la observación empírica de que la eliminación de la conversión estándar corregido el error.
    • he añadido un ejemplo a mi respuesta, que es más fácil de entender sin necesidad de que todos los operadores de las reglas de todo, creo.

Dejar respuesta

Please enter your comment!
Please enter your name here