2016-05-13 25 views
10

Stavo creando una classe derivata sottile con un costruttore di inoltro. (Portami dietro, devo usare GCC 4.7.2, che manca di costruttori ereditati).Perché questo operatore = chiamata ambigua?

Al primo tentativo, ho dimenticato di aggiungere la parola chiave explicit e ho ricevuto un errore. Qualcuno potrebbe spiegare esattamente perché si verifica questo particolare errore? Sto avendo problemi a capire la sequenza degli eventi.

#include <memory> 

template<typename T> 
struct shared_ptr : std::shared_ptr<T> 
{ 
    template<typename...Args> 
    /*explicit*/ shared_ptr(Args &&... args) 
    : std::shared_ptr<T>(std::forward<Args>(args)...) 
    {} 
}; 

struct A {}; 

struct ConvertsToPtr 
{ 
    shared_ptr<A> ptr = shared_ptr<A>(new A()); 
    operator shared_ptr<A> const &() const { return ptr; } 
}; 

int main() 
{ 
    shared_ptr<A> ptr; 
    ptr = ConvertsToPtr(); // error here 
    return 0; 
} 

L'errore:

test.cpp: In function ‘int main()’: 
test.cpp:28:23: error: ambiguous overload for ‘operator=’ in ‘ptr = ConvertsToPtr()’ 
test.cpp:28:23: note: candidates are: 
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(const shared_ptr<A>&) 
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(shared_ptr<A>&&) 

risposta

7

Questo è anche il caso di g++ 4.8.4 con il seguente:
g++ -g -pedantic --std=c++11 -o test main.cpp
Le impostazioni VS2015 sono tutti inadempienti.

Il problema è che il compilatore tenta di convertire un valore temporaneo restituito da ConvertsToPtr() a un oggetto shared_ptr. Quando il compilatore viene utilizzato con la parola chiave explicit, questa conversione non si verifica mai utilizzando il costruttore. Tuttavia, durante l'esame con gdb sembra che invece sta usando la funzione di conversione shared_ptr<A> const &() per abbinare il Tipo appropriato. Questa conversione restituisce quindi un valore const shared_ptr & che non ha ambiguità quando si richiama l'operatore di assegnazione (anche questo corrisponde ai risultati di wojciech Frohmberg).

Tuttavia, se si omette explicit, viene restituito un oggetto di shared_ptr. questo può essere abbinato o alla versione rvalue dell'operatore di assegnazione o alla versione del lvalue.

Secondo N4296, Tabella 11, quindi abbiamo, dopo la costruzione con lo conversion constructor, un oggetto rvalue of shared_ptr. Tuttavia, la risoluzione di sovraccarico trova due corrispondenze, entrambe di livello inferiore a Exact Match (la versione di rvalue è Identity matching mentre l'altra è inferiore a Qualification matching).

Ho controllato anche su VS2015 e come indicato nei commenti, funziona. Tuttavia, usando il comando di debug di , il valore viene assegnato per priorità al valore . omologo della versione di rifrazione del valore costante.

MODIFICA: Ho guardato un po 'più in profondità nello standard e aggiungere la modifica. il testo eliminato relativo ai risultati VS2015 era errato, perché non ho definito entrambi i compiti. Quando entrambi i compiti sono stati dichiarati, preferisce il valore.

Suppongo che il compilatore VS distingue lo Identity dalla corrispondenza Qualification nella classifica. Tuttavia, come ho concluso, è il compilatore VS che è bacato. i compilatori g++ obbediscono allo standard indicato. Tuttavia dal momento che GCC 5.0 funziona come Visual Studio, la possibilità di bug del compilatore è sottile, quindi sarei felice di vedere altri approfondimenti di esperti.

MODIFICA: in 13.3.3.2 uno degli interruttori pareggio, dopo la classifica migliore che ho scritto su di esso, è:

— S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.

V'è un esempio attaccato mostrando che un dato rvalue (riferimento non rvalue) dovrebbe corrispondere a un const int && sopra const int &. Pertanto, suppongo, è lecito ritenere che sia rilevante per il nostro caso, anche se abbiamo il tipo && e non il tipo const &&. Immagino, dopo tutto, che GCC 4.7.4.8 sia bacato dopo tutto.

+0

Sicuramente i rvalues ​​si legano ai riferimenti costanti lvalue. Prova a chiamare 'void f (float const &)' con '0f'. – AndyJost

+1

Quindi dopo la costruzione di 'ConvertsToPtr' il compilatore cerca' shared_ptr :: operator = 'e procede con la risoluzione di sovraccarico. Il candidato che accetta 'shared_ptr const &' succede a causa dell'operatore di conversione. Senza 'explicit', anche il candidato che accetta' shared_ptr &&' riesce tramite la conversione basata sul costruttore. Nessuna conversione è migliore, quindi la chiamata è ambigua. Ha senso. – AndyJost

+0

@AndyJost: Il percorso di conversione più breve è chiaramente migliore, poiché C++ non consente due conversioni definite dall'utente nella sequenza, quindi questo è un bug del compilatore. –

Problemi correlati