Considere el siguiente código .
struct any
{
template <typename T>
operator T &&() const;
template <typename T>
operator T &() const;
};
int main()
{
int a = any{};
}
Aquí el segundo operador de conversión es elegido por la resolución de sobrecarga. ¿Por qué?
Por lo que yo entiendo, los dos operadores se deducen operator int &&() const
y operator int &() const
respectivamente. Ambos están en el conjunto de funciones viables. Leer a través de [over.match.best] no me ayudó a entender por qué este último es mejor.
¿Por qué la última función es mejor que la primera?
template <typename T> operator T &&() const &&; template <typename T> operator T &() const &;
consigue que llame al primero.Respuestas:
Se
T&
prefiere el operador de conversión que regresa porque es más especializado que el operador de conversión que regresaT&&
.Ver C ++ 17 [temp.deduct.partial] / (3.2):
y / 9:
fuente
Los operadores de conversión de valor de retorno deducidos son un poco extraños. Pero la idea central es que actúa como un parámetro de función para elegir cuál se usa.
Y al decidir entre
T&&
yT&
lasT&
victorias en las reglas de resolución de sobrecarga. Esto es para permitir:trabajar.
T&&
puede coincidir con un valor, pero cuando están disponibles las sobrecargas de referencia universal y de valor, se prefiere el valor.El conjunto correcto de operadores de conversión es probablemente:
o incluso
para evitar que una extensión fallida de por vida te muerda.
[RECORTE]
Que luego termina dependiendo de las reglas "más especializadas" al elegir sobrecargas:
Por
operator T&&
lo tanto, no es al menos tan especializado comooperator T&
, mientras que ningún estado de reglaoperator T&
no es al menos tan especializado comooperator T&&
, por lo queoperator T&
es más especializado queoperator T&&
.Más plantillas especializadas ganan resolución de sobrecarga por menos, todo lo demás es igual.
fuente
&&
vs&
cuando se seleccionan las sobrecargas de plantillas, pero extraer el texto exacto llevaría un tiempo .Estamos tratando de inicializar un
int
de unany
. El proceso para eso es:Averigua cómo podemos hacer eso. Es decir, determinar todos nuestros candidatos. Estos provienen de las funciones de conversión no explícitas a las que se puede convertir
int
mediante una secuencia de conversión estándar ( [over.match.conv] ). La sección contiene esta frase:Elige el mejor candidato.
Después del paso 1, tenemos dos candidatos.
operator int&() const
yoperator int&&() const
, los cuales se considera que rindenint
con el propósito de seleccionar funciones candidatas. ¿Cuál es el mejor candidato que rindeint
?Tenemos un desempate que prefiere las referencias de valor a las referencias de valor ( [over.ics.rank] /3.2.3 ). Sin embargo, en realidad no estamos vinculando una referencia aquí, y el ejemplo allí está algo invertido; esto es para el caso en el que el parámetro es una referencia de lvalue vs rvalue.
Si eso no se aplica, entonces pasamos al desempate [over.match.best] /2.5 de preferir la plantilla de función más especializada.
En términos generales, la regla general es que la conversión más específica es la mejor coincidencia. La función de conversión de referencia lvalue es más específica que la función de conversión de referencia de reenvío, por lo que es preferible. No hay nada acerca de la
int
inicialización que requiera un valor r (si hubiéramos estado inicializando un valorint&&
, entoncesoperator T&() const
no habría sido un candidato).fuente
T&&
victorias ¿no? Ah, pero no tenemos un valor al que depender. O nosotros? Al elegir a nuestros candidatos, algunas palabras deben haber definido cómo llegamosint&
yint&&
, ¿fue vinculante?