2011-01-26 19 views
6

Prima di continuare a leggere questo, leggere prima Is there a difference in C++ between copy initialization and direct initialization?, assicurarsi di aver capito di cosa sta parlando.Inizializzazione della copia C++ e inizializzazione diretta, caso strano

cercherò di riassumere la regola qui prima (lettura standard n3225 8,5/16, 13.3.1.3, 13.3.1.4, 13.3.1.5 e),

1) Per l'inizializzazione diretta, tutti i costruttori saranno considerati come il set di sovraccarico, la risoluzione di sovraccarico selezionerà il migliore in base alle regole di risoluzione di sovraccarico.

2) Per l'inizializzazione della copia e il tipo di origine è uguale al tipo di destinazione o derivato dal tipo di destinazione, la regola è uguale a precedente salvo che solo i costruttori di conversione (costruttori senza esplicito) verranno considerati come set di overload. Questo in realtà significa che i costruttori di copia/spostamento espliciti non saranno considerati nel set di overload.

3) Per i casi di inizializzazione della copia non inclusi in (2) sopra (il tipo di origine è diverso dal tipo di destinazione e non derivato dal tipo di destinazione), consideriamo prima le sequenze di conversione definite dall'utente che possono convertire dal tipo di origine al tipo di destinazione o (quando viene utilizzata una funzione di conversione) a una classe derivata. Se la conversione ha esito positivo, il risultato viene utilizzato per inizializzazione diretta l'oggetto di destinazione.

3.1) Durante questa sequenza di conversione definita dall'utente, verranno considerati sia i cori di conversione (non espliciti) che le funzioni di conversione non esplicite, in base alle regole in 8.5/16 e 13.3.1.4.

3.2) Il valore del risultato sarà inizializzazione diretta l'oggetto di destinazione, come regole elencate in (1), vedere 8.5/16.

Ok, abbastanza per le regole, diamo un'occhiata ad un codice strano, che non ho davvero idea di dove il mio ragionamento sia sbagliato, o che tutti i compilatori abbiano torto. Per favore aiutami, grazie.

struct A 
{ 
    A (int) { } 
    A() { } 
    explicit A(const A&) { } 
}; 
struct B 
{ 
    operator A() { return 2; } 
    //1) visual c++ and clang passes this 
    //gcc 4.4.3 denies this, says no viable constructor available 
}; 
int main() 
{ 
    B b; 
    A a = b; 
    //2) oops, all compilers deny this 
} 

Nella mia comprensione, per (1),

operator A() { return 2; } 

Poiché C++ ha una regola che funziona ritorno è preso come copia-inizializzazione, secondo la regola precedente, 2 saranno dapprima implicitamente convertito ad A, che dovrebbe essere OK perché A ha un costruttore A (int). Quindi il valore provvisorio convertito verrà utilizzato per inizializzare direttamente l'oggetto restituito, il che dovrebbe essere corretto anche perché l'inizializzazione diretta può fare uso del costruttore di copie esplicito. Quindi GCC ha torto.

Per (2),

A a = b; 

Nella mia comprensione, innanzitutto b viene convertito implicitamente A, dall'operatore A(), e quindi il valore convertito è utilizzato per direct-inizializzare una, che può Chiama ovviamente il costruttore di copie esplicite? Quindi questo dovrebbe passare la compilazione e tutti i compilatori sono sbagliati?

Nota che per (2), sia visual C++ che clang ha un errore simile a, "Errore, impossibile convertire da B a A", ma se rimuovo la parola chiave esplicita nel costruttore di copie di A, l'errore è andato ..

Grazie per la lettura.


modificare 1

perché qualcuno ancora non ha ottenuto quello che volevo dire, cito il seguente norma da 8,5/16,

In caso contrario (cioè, per i restanti della copia casi di inizializzazione), sequenze di conversione definite dall'utente che possono convertire dal tipo di origine a il tipo di destinazione o (quando viene utilizzata una funzione di conversione ) aLa classe derivataè elencata come descritto in 13.3.1.4, e la migliore viene scelta tramite la risoluzione di sovraccarico (13.3). Se la conversione non può essere eseguita o è ambigua, l'inizializzazione non è corretta. La funzione selezionata viene chiamata con l'espressione di inizializzazione come argomento ; se la funzione è un costruttore , la chiamata inizializza un temporaneo della versione cv-non qualificata del tipo di destinazione. Il temporaneo è un valore di prvalore. Il risultato della chiamata (che è il temporaneo per caso costruttore) viene poi utilizzato per diretta inizializzazione, secondo le regole di cui sopra , l'oggetto che è la destinazione del copia-inizializzazione. In alcuni casi, a è consentita l'implementazione di eliminare la copia inerente a questa inizializzazione diretta creando il risultato intermedio direttamente in l'oggetto da inizializzare; vedi 12.2, 12.8.

Si noti che ha menzionato l'inizializzazione diretta dopo la conversione definita dall'utente. Il che significa, a mio modo di vedere, che il codice seguente obbedirà alle regole come quello che ho commentato, il che è confermato da clang, coomeau online, visual C++, ma GCC 4.4.3 fallisce sia (1) che (2). Anche se questa è una regola strana, ma segue il ragionamento dallo standard.

struct A 
{ 
    A (int) { } 
    A() { } 
    explicit A(const A&) { } 
}; 

int main() 
{ 
    A a = 2; //1)OK, first convert, then direct-initialize 
    A a = (A)2; //2)oops, constructor explicit, not viable here! 
} 
+0

Comeau Online accetta l'intero snippet di codice come scritto. –

+0

ok, grazie. Questo mi dà una certa sicurezza che il mio ragionamento non stia andando nella direzione sbagliata.^_^ – user534498

+0

@James McNellis: Tuttavia, Comeau Online rifiuta il codice nella mia risposta, anche se secondo il ragionamento di OP dovrebbe essere accettato. – AnT

risposta

8

È dichiarato la propria copia costruttore di explicit (a proposito, perché?), Il che significa che non può più essere utilizzato per la copia implicita di oggetti di classe. Per utilizzare questo costruttore per la copia ora sei obbligato a usare la sintassi di inizializzazione diretta. Vedere 12.3.1/2

2 Un costruttore esplicito costruisce oggetti proprio come i costruttori non esplicite, ma lo fa solo se la sintassi di inizializzazione diretta (8.5) o dove calchi (5.2.9, 5.4) sono esplicitamente Usato.

Il problema può essere illustrato dal seguente esempio molto più breve

struct A { 
    A() {} 
    explicit A(const A&) {} 
}; 

int main() { 
    A a; 
    A b = a; // ERROR: copy-initialization 
    A c(a); // OK: direct-initialization 
} 

'quanto blocca tutte le conversioni da lavorare, poiché tutti si basano su copia-inizializzazione, che a sua volta si basa su copia implicita. E hai disattivato la copia implicita.

Inoltre, vedere lo Defect Report #152 che copre questo problema specifico. Anche se non sono sicuro di quali sarebbero le conseguenze della "risoluzione proposta" ...

+0

ciao, non hai letto le regole dallo standard 8.5/16. Durante l'inizializzazione della copia, l'inizializzazione diretta verrà utilizzata dopo la conversione definita dall'utente, che ovviamente sarà autorizzata a utilizzare il costruttore di copie esplicite. – user534498

+0

@ user534498: beh, 12.3.1/2 sembra richiedere la sintassi di inizializzazione diretta * * per rendere espliciti i costruttori espliciti. Solo una semplice inizializzazione diretta non è sufficiente, la * sintassi * deve essere presente nel codice. E non è presente nel tuo. – AnT

+0

Ciao Andrey, per favore leggi la mia modifica 1. btw, penso che l'esempio in 12.3.1/2 non contraddica il mio ragionamento sopra perché non implica il costruttore di copie esplicito. – user534498

0

Come hai detto, gcc non compila il seguente codice.

struct A { 
    A(int) {} 
    explicit A(A const&) {} 
}; 

int main() { 
    A a = 2; 
} 

Penso che questo non sia conforme allo standard standard 8.5 p15.
Tuttavia, come per il primo caso,

struct A { 
    A(int) {} 
    explicit A(A const&) {} 
}; 

struct B { 
    operator A() { return 2; } 
}; 

int main() { 
    B b; 
    A a = b; 
} 

io non sono convinto che permette questo è conforme.
Come forse sapete, return invocherà la copia due volte concettualmente.
return 2; costruisce uno A implicitamente da un int 2 ed è utilizzato per inizializzazione diretta di un valore di ritorno temporale (R).
Tuttavia, l'inizializzazione diretta deve essere applicata alla seconda copia da R a a?
Non sono riuscito a trovare la dicitura nello standard corrente che afferma esplicitamente che l'inizializzazione diretta deve essere applicata due volte.
Poiché è certo che questa sequenza guasta le specifiche explicit in un senso , non sono sorpreso anche se gli sviluppatori di compilatori pensassero che consentire questo è un difetto.

Lasciatemi fare un'aggiunta superflua.
Un comportamento non conforme del compilatore non significa che il compilatore abbia un difetto direttamente.
Lo standard presenta già difetti come mostrano i rapporti sui difetti. Ad esempio, lo standard C non consente la conversione da un puntatore a un array di tipo T, a un puntatore a un array di const T.
Questo è consentito in C++ e penso che dovrebbe essere consentito semanticamente in C allo stesso modo.
Gcc emette un avviso su questa conversione. Comeau genera un errore e non viene compilato.

Problemi correlati