2016-12-10 3 views
10

La question est passée dans le contexte de this answer.Pourquoi l'initialisation de la liste directe provoque-t-elle une ambiguïté pour la référence de type cast si les opérateurs de cast du type et la référence au type sont déclarés?

Prenons un exemple:

struct foo { 
    int value; 
    operator int&(){ return value; } 
    operator int(){ return value; } 
}; 

int main() { 
    int &a(foo{}); // #1 
    //int &b{foo{}}; // #2 -- ambiguity 
    int &c = foo{}; // #3 
    //int &d = {foo{}}; // #4-- ambiguity 
    int &d { a }; // #5 
    int &e = { a }; // #6 
    (void)a; 
    (void)c; 
    (void)d; 
    (void)e; 
} 

Je ne comprends pas pourquoi est-# 2 et # 4 cause de l'ambiguïté alors que # 1 et # 3 ne fonctionne pas. La question est donc la suivante: pourquoi l'initialisation directe de la liste entraîne-t-elle une ambiguïté pour la distribution implicite afin de faire référence si les opérateurs de conversion au type et la référence au type sont déclarés?

+2

'int & b {foo {}}' est l'initialisation directe * list *. Ce ne sont pas la même chose. –

Répondre

7

L'initialisation de la liste, lorsqu'elle est utilisée pour initialiser une référence, prendra les valeurs listées et les convertira en prvalue (alias: temporaire), qui sera utilisée pour initialiser la référence.

Donc int &b{foo{}} est fonctionnellement équivalent à int &b(int(foo{})). Ce qui est ambigu; il pourrait générer int via operator int ou operator int&. Mais même si ce n'était pas ambigu, vous obtiendriez toujours une référence lvalue non-const à une prvalue. Ce qui est illégal. Donc, ce code était jamais aller au travail.

listes contreventés-init-(accolades) initialisation des objets, et non références à des objets. Si vous avez déjà un objet et que vous voulez en obtenir une référence, n'utilisez pas de braced-init-lists.


Mais dans ce cas, pourquoi ne compilateur accepte # 5?

Parce que l'initialisation de liste est une série de règles avec priorité. Une règle qui a une priorité plus élevée que celle que j'ai indiquée ci-dessus est le cas d'une liste braced-init-list qui contient une seule valeur, dont le type est identique au type de ce qui est initialisé. # 5 et 6 juste arriver à correspondre à cette facture, puisque d, e, et a sont tous int& s. Mais si vous suivez mon conseil et n'utilisez pas les listes init-braced lorsque vous n'essayez pas de créer un objet, vous n'aurez pas à vous inquiéter des cas de coin comme ça. `Int & a (foo {})` est une initialisation directe.

+3

Comme tout le monde ne connaît pas prvalue (par exemple, moi-même). Voici le lien vers son explication: http://stackoverflow.com/a/3601661/783510 –

+0

Mais dans ce cas, pourquoi le compilateur accepte-t-il le # 5? n'est-ce pas équivalent de 'int & d (int (a));'? –

+1

@ W.F .: Voir mes modifications pour votre réponse. –