Considérez le code suivant .
struct any
{
template <typename T>
operator T &&() const;
template <typename T>
operator T &() const;
};
int main()
{
int a = any{};
}
Ici, le deuxième opérateur de conversion est choisi par la résolution de surcharge. Pourquoi?
D'après ce que je comprends, les deux opérateurs sont déduits de operator int &&() const
et operator int &() const
respectivement. Les deux font partie de l'ensemble des fonctions viables. La lecture de [over.match.best] ne m'a pas aidé à comprendre pourquoi ce dernier est meilleur.
Pourquoi cette dernière fonction est-elle meilleure que la première?
template <typename T> operator T &&() const &&; template <typename T> operator T &() const &;
fait appeler le premier.Réponses:
L'opérateur de conversion qui retourne
T&
est préféré car il est plus spécialisé que l'opérateur de conversion qui retourneT&&
.Voir C ++ 17 [temp.deduct.partial] / (3.2):
et / 9:
la source
Les opérateurs de conversion de valeur de retour déduits sont un peu étranges. Mais l'idée centrale est qu'il agit comme un paramètre de fonction pour choisir lequel est utilisé.
Et pour décider entre
T&&
etT&
lesT&
victoires dans les règles de résolution de surcharge. Ceci permet:travailler.
T&&
peut correspondre à une valeur l, mais lorsque la surcharge lvalue et la référence universelle sont disponibles, la valeur lvalue est préférée.Le bon ensemble d'opérateurs de conversion est probablement:
ou même
pour éviter que l'extension de vie échouée ne vous mord.
[COUPER]
Ce qui finit alors par dépendre de règles "plus spécialisées" lors du choix des surcharges:
Ainsi, il
operator T&&
n'est pas au moins aussi spécialisé queoperator T&
, pendant ce temps, aucun état de règleoperator T&
n'est pas au moins aussi spécialisé queoperator T&&
, donc iloperator T&
est plus spécialisé queoperator T&&
.Des modèles plus spécialisés gagnent moins de résolution de surcharge, toutes choses étant égales par ailleurs.
la source
&&
vs&
est traité lors du choix des surcharges de modèles, mais taquiner le texte exact prendrait un certain temps .Nous essayons d'initialiser un à
int
partir d'unany
. Le processus pour cela:Découvrez tout ce que nous pouvons faire. Autrement dit, déterminez tous nos candidats. Celles-ci proviennent des fonctions de conversion non explicites qui peuvent être converties en
int
via une séquence de conversion standard ( [over.match.conv] ). La section contient cette phrase:Choisissez le meilleur candidat.
Après l'étape 1, nous avons deux candidats.
operator int&() const
etoperator int&&() const
, les deux sont considérés comme cédantint
aux fins de la sélection des fonctions candidates. Quel est le meilleur candidat qui donneint
?Nous avons un bris d'égalité qui préfère les références lvalue aux références rvalue ( [over.ics.rank] /3.2.3 ). Nous ne lions pas vraiment une référence ici cependant, et l'exemple il est quelque peu inversé - c'est pour le cas où le paramètre est une référence lvalue vs rvalue.
Si cela ne s'applique pas, nous passons au bris d'égalité [over.match.best] /2.5 de préférer le modèle de fonction plus spécialisé.
De manière générale, la règle générale est que la conversion la plus spécifique est la meilleure correspondance. La fonction de conversion de référence lvalue est plus spécifique que la fonction de conversion de référence de transfert, elle est donc préférable. Il n'y a rien dans l'
int
initialisation qui nécessite une valeur r (si nous avions plutôt initialisé uneint&&
, alors l'operator T&() const
aurait pas été candidat).la source
T&&
victoires ne le sont pas? Ah, mais nous n'avons pas de valeur à laquelle nous sommes liés. Ou le faisons-nous? Lors de la sélection de nos candidats, certains mots doivent avoir défini comment nous avons obtenuint&
etint&&
, était-ce une obligation?