5 votes

LValue ref fonction membre qualifiée appelée sur un objet RValue

J'essaie de comprendre pourquoi l'extrait suivant appelle la surcharge de l'opérateur LValue cast :

#include <iostream>

class Foo
{
public:
    Foo(int i = 0) : i(i) {}

    operator const int& () const &
    {
        std::cout << "lvalue\n";
        return i;
    }

    operator int () const &&
    {
        std::cout << "rvalue\n";
        return i;
    }

    int i = 0;
};

Foo Fool()
{
    return Foo(5);
}

int main()
{
    const int& i = Fool();
    const int j = Fool();

    return 0;
}

Les sorties actuelles sont :

lvalue

rvalue

Mais d'après ce que j'ai compris Fool() renvoie un rvalue et puisque const& peut se lier à rvalues il n'est pas nécessaire de construire un lvalue Foo .

Quelqu'un peut-il expliquer pourquoi lvalue est en cours de construction ? Je crois que c'est un problème lvalue .

2voto

StoryTeller Points 6139

Ok, donc la chose à noter ici est que la résolution des surcharges ne prend en compte qu'une seule fonction de conversion pour i . Ils ne participent pas tous les deux, et le qualificatif de référence ne peut donc pas être utilisé pour les différencier. Pour le cas de la liaison d'une référence

[over.match.ref]

Dans les conditions précisées dans [dcl.init.ref], une référence peut être directement liée au résultat de l'application d'une fonction de conversion à une expression d'initialisation. expression initialisatrice. La résolution de surcharge est utilisée pour sélectionner la fonction de conversion à invoquer. Si l'on suppose que "référence à cv1 T" est le type de la référence initialisée. est le type de la référence initialisée et "cv S" est le type de l'expression d'initialisation, avec S. de l'expression de l'initialisateur, avec S comme type de classe, les fonctions candidates sont sélectionnées comme suit :

  • Les fonctions de conversion de S et de ses classes de base sont considérées. Les fonctions de conversion non explicites qui ne sont pas cachées dans S et qui donnent le type "référence lvalue à cv2 T2" (lors de l'initialisation d'une référence lvalue à cv2 T2). référence l valeur de référence ou une référence rvalue à la fonction) ou "cv2 T2" ou "rvalue reference to cv2 T2" (lors de l'initialisation d'une référence rvalue ou ou d'une référence lvalue à une fonction), où "cv1 T" est compatible par référence avec "cv2 T2", sont des fonctions candidates. Pour l'initialisation directe, les fonctions de conversion explicites qui ne sont pas ne sont pas cachées dans S et donnent le type "référence lvalue à cv2 T2" (lors de l'initialisation d'une référence lvalue). l'initialisation d'une référence lvalue ou une référence rvalue à la fonction) ou "rvalue reference to cv2 T2" (lors de l'initialisation d'une référence rvalue ou une référence lvalue à une fonction), où T2 est du même type que T ou peut être converti en type T avec une conversion de qualification, sont également des fonctions candidates.

Selon le texte en caractères gras, lors de l'initialisation i notre uniquement le candidat est operator int const& . Ainsi, la résolution des surcharges peut soit passer ici, soit échouer complètement. Mais elle ne peut pas sélectionner operator int puisque celle-ci n'est même pas envisagée. Elle réussit parce qu'une référence lvalue qualifiée const peut se lier à l'argument objet.

Par contre, pour initialiser une valeur

[over.match.conv]

Dans les conditions spécifiées dans [dcl.init], dans le cadre d'une initialisation d'un objet de type non-classe, une fonction de conversion peut être invoquée pour convertir une expression d'initialisation de type classe en le type de l'objet en cours d'initialisation. La résolution de surcharge est utilisée pour sélectionner la fonction de conversion à invoquer. Si l'on suppose que "cv1 T" est le type de l'objet initialisé est le type de l'objet en cours d'initialisation, et "cv S" est le type de l'expression de l'initialisateur, avec S comme type de classe. l'expression de l'initialisateur, avec S comme type de classe, les fonctions candidates sont sélectionnées comme suit :

  • Les fonctions de conversion de S et de ses classes de base sont prises en compte. Les fonctions de conversion non explicites qui ne sont pas cachées dans S et qui produisent le type T ou un type qui peut être converti en type T via une séquence de conversion standard sont des fonctions candidates. séquence de conversion standard sont des fonctions candidates. Pour l'initialisation directe, les fonctions de conversion explicites qui ne sont pas ne sont pas cachées dans S et qui donnent le type T ou un type qui peut être converti en type T avec une conversion de qualification sont aussi des fonctions candidates. candidates. Les fonctions de conversion qui renvoient un type qualifié cv sont sont considérées comme donnant la version cv non qualifiée de ce type dans le cadre de ce processus de sélection des fonctions candidates. Un appel à une fonction de conversion renvoyant "référence à X" est une valeur gluante de type X, et une telle fonction de conversion fonction de conversion est donc considérée comme produisant X pour ce processus de sélection des fonctions candidates.

Ainsi, lors de l'initialisation j les deux fonctions de conversion participent en tant que surcharges, et ici le qualificatif de référence fait une différence.

Il y a bien une référence qui pendouille ici, et cela semble être dû à un coin sombre de la langue. La puce du premier paragraphe cité pourrait probablement être affinée pour prendre en compte la liaison de const Les références lvlaue sont meilleures. Puisque celles-ci peuvent également être liées à des temporaires, votre deuxième opérateur de conversion pourrait idéalement être un candidat avec de meilleures règles.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X