c++ operator-overloading multiple-inheritance ambiguous

c++ - GCC no puede diferenciar entre operador++() y operador++(int)



operator-overloading multiple-inheritance (1)

template <typename CRTP> struct Pre { CRTP & operator++(); }; template <typename CRTP> struct Post { CRTP operator++(int); }; struct Derived : Pre<Derived> , Post<Derived> {}; int main() { Derived d; d++; ++d; }

Obtengo estos errores de GCC:

<source>: In function ''int main()'': <source>:18:10: error: request for member ''operator++'' is ambiguous d++; ^~ <source>:8:14: note: candidates are: CRTP Post<CRTP>::operator++(int) [with CRTP = Derived] CRTP operator++(int); ^~~~~~~~ <source>:3:16: note: CRTP& Pre<CRTP>::operator++() [with CRTP = Derived] CRTP & operator++(); ^~~~~~~~ <source>:19:11: error: request for member ''operator++'' is ambiguous ++d; ^ <source>:8:14: note: candidates are: CRTP Post<CRTP>::operator++(int) [with CRTP = Derived] CRTP operator++(int); ^~~~~~~~ <source>:3:16: note: CRTP& Pre<CRTP>::operator++() [with CRTP = Derived] CRTP & operator++(); ^~~~~~~~

Los operadores de pre-decremento y post-decremento causan errores similares. No hay tales errores con Clang. ¿Alguna idea de qué podría estar mal o cómo solucionar esto?


La búsqueda de nombres debe ocurrir primero. En este caso para el operator++ del nombre operator++ .

[basic.lookup] (énfasis mío)

1 Las reglas de búsqueda de nombres se aplican uniformemente a todos los nombres (incluidos typedef-names ([dcl.typedef]), namespace-names ([basic.namespace]), y class-names ([class.name])) siempre que la gramática lo permita dichos nombres en el contexto discutido por una regla particular. La búsqueda de nombres asocia el uso de un nombre con una declaración ([basic.def]) de ese nombre. La búsqueda de nombres encontrará una declaración inequívoca para el nombre (consulte [class.member.lookup]) . La búsqueda de nombres puede asociar más de una declaración con un nombre si encuentra que el nombre es un nombre de función; se dice que las declaraciones forman un conjunto de funciones sobrecargadas ([sobrecarga]). La resolución de sobrecarga ([over.match]) tiene lugar después de que la búsqueda de nombres haya tenido éxito . Las reglas de acceso (Cláusula [class.access]) se consideran solo una vez que la búsqueda de nombres y la resolución de sobrecarga de funciones (si corresponde) han tenido éxito. Solo después de que la búsqueda de nombres, la resolución de la sobrecarga de la función (si corresponde) y la verificación de acceso hayan tenido éxito, los atributos introducidos por la declaración del nombre se utilizan más en el procesamiento de expresiones (Cláusula [expr]).

Y solo si la búsqueda no es ambigua, la resolución de sobrecarga continuará. En este caso, el nombre se encuentra en el alcance de dos clases diferentes, por lo que existe una ambigüedad incluso antes de la resolución de sobrecarga.

[class.member.lookup]

8 Si el nombre de una función sobrecargada se encuentra sin ambigüedad, la resolución de sobrecarga ([over.match]) también tiene lugar antes del control de acceso. Las ambigüedades a menudo se pueden resolver calificando un nombre con su nombre de clase. [Ejemplo:

struct A { int f(); }; struct B { int f(); }; struct C : A, B { int f() { return A::f() + B::f(); } };

- ejemplo final]

El ejemplo resume bastante bien las reglas de búsqueda bastante largas en los párrafos anteriores de [class.member.lookup]. Hay una ambigüedad en tu código. GCC tiene razón en reportarlo.

En cuanto a trabajar alrededor de esto, las personas en los comentarios ya presentaron las ideas para una solución. Añadir una clase CRTP auxiliar

template <class CRTP> struct PrePost : Pre<CRTP> , Post<CRTP> { using Pre<CRTP>::operator++; using Post<CRTP>::operator++; }; struct Derived : PrePost<Derived> {};

El nombre ahora se encuentra en el alcance de una sola clase y nombra ambas sobrecargas. La búsqueda es exitosa y la resolución de sobrecarga puede continuar.