zapatos tag ropa preguntas los closet c++ c++11 overloading language-lawyer ref-qualifier

c++ - tag - Resolución de sobrecarga con ref-calificadores.



tag de los zapatos preguntas (1)

Mientras trabajo con sobrecargas de funciones calificadas por ref, obtengo resultados diferentes de GCC (4.8.1) y Clang (2.9 y tronco) . Considere el siguiente código:

#include <iostream> #include <utility> struct foo { int& bar() & { std::cout << "non-const lvalue" << std::endl; return _bar; } //~ int&& bar() && //~ { //~ std::cout << "non-const rvalue" << std::endl; //~ return std::move(_bar); //~ } int const& bar() const & { std::cout << "const lvalue" << std::endl; return _bar; } int const&& bar() const && { std::cout << "const rvalue" << std::endl; return std::move(_bar); } int _bar; }; int main(int argc, char** argv) { foo().bar(); }

Clang lo compila y genera "const rvalue" , mientras que GCC cree que esta es una llamada ambigua, ya que las dos funciones constantes son las mejores candidatas viables. Si proporciono las 4 sobrecargas, ambos compiladores emiten "non-const rvalue" .

Me gustaría saber qué compilador, si es que está haciendo lo correcto, y cuáles son las piezas estándar relevantes en juego.

Nota: la razón por la que esto realmente importa es que el código real declara que ambas funciones constexpr como constexpr . Por supuesto, no hay salida para std::cout y se usa static_cast lugar de std::move , por lo que son definiciones constexpr válidas. Y como en C ++ 11 constexpr todavía implica const , la sobrecarga comentada en el código de ejemplo no se puede proporcionar, ya que redefiniría la sobrecarga de rvalue calificada por const.


En primer lugar, el parámetro objeto implícito se trata como un parámetro normal según 13.3.1.4:

Para funciones miembro no estáticas, el tipo del parámetro objeto implícito es

- "lvalue reference to cv X" para funciones declaradas sin un ref-qualifier o con el & ref-qualifier

- "rvalue reference to cv X" para funciones declaradas con el & & ref-qualifier

donde X es la clase de la cual la función es miembro y cv es la calificación cv en la declaración de la función miembro.

Entonces lo que estás preguntando es equivalente a lo siguiente:

void bar(foo&); void bar(foo&&); void bar(const foo&); void bar(const foo&&); int main() { bar(foo()); }

La expresión foo() es un valor de clase.

En segundo lugar, la versión de referencia de valor no constante no es viable, ya que un valor predefinido no puede vincularse a él.

Esto nos deja con tres funciones viables para la resolución de sobrecargas.

Cada uno tiene un único parámetro de objeto implícito ( const foo& , foo&& o const foo&& ), por lo que debemos clasificar estos tres para determinar la mejor coincidencia.

En los tres casos es un enlace de referencia directamente vinculado . Esto se describe en declaradores / inicialización (8.5.3).

La clasificación de los tres enlaces posibles ( const foo& , foo&& y const foo&& ) se describe en 13.3.3.2.3:

La secuencia de conversión estándar S1 es una mejor secuencia de conversión que la secuencia de conversión estándar S2 si

  • S1 y S2 son enlaces de referencia y ninguno se refiere a un parámetro de objeto implícito de una función miembro no estática declarada sin un calificador ref [esta excepción no se aplica aquí, todos tienen calificadores ref], y S1 vincula una referencia rvalue a un valor de r [un valor de clase es un valor de r] y S2 se une a una referencia de valor de l .

Esto significa que tanto foo&& como const foo&& son mejores que const foo& .

  • S1 y S2 son enlaces de referencia, y los tipos a los que se refieren las referencias son del mismo tipo, excepto para los calificadores cv de nivel superior, y el tipo al que se refiere la referencia inicializada por S2 es más calificado que el tipo al que La referencia inicializada por S1 se refiere .

Esto significa que foo&& es mejor que const foo&& .

Así que Clang tiene razón, y es un error en GCC. La clasificación de sobrecarga para foo().bar() es la siguiente:

struct foo { int&& bar() &&; // VIABLE - BEST (1) int const&& bar() const &&; // VIABLE - (2) int const& bar() const &; // VIABLE - WORST (3) int& bar() &; // NOT VIABLE int _bar; };

El error en GCC parece aplicarse puramente a parámetros de objetos implícitos (con ref-qualifiers ), para un parámetro normal parece obtener la clasificación correcta, al menos en 4.7.2.