2010-01-05 18 views
7

Si consideri il seguente codice:C++ costruttori espliciti e iteratori

In assenza di tale compilazione? La mia sensazione è che non dovrebbe, a causa della marcatura del costruttore explicit.

Microsoft Visual C++ è d'accordo, dando un messaggio di errore chiaro: cannot convert from 'int' to 'const A'; Constructor for struct 'A' is declared 'explicit'

Tuttavia, utilizzando Comeau's online compiler, il codice viene compilato con successo.

Quale è corretto?

Edit:

È interessante notare che, cambiando vector-set (dopo l'aggiunta di un operator < alla A) fa sì che entrambi i compilatori per dare un errore.

Tuttavia, la modifica vector<int> a map<int, int> e vector<A>-map<A, A> fa sì che entrambi i compilatori di accettare il codice!

+0

Risposta indiretta qui: http://stackoverflow.com/questions/1943228/implicit-constructor-conversion-works-on-explicit-vectorvector-only-sometimes –

+1

Ho appena scritto una risposta in base a tale domanda + risposta, ma in realtà non si applica, dal momento che 'std :: vector :: iterator' non è un tipo integrale e il compilatore di riempimento non viene chiamato. Quindi l'ho cancellato. Penso che tutto quello che posso dire è che lo standard non * proibisce * la conversione esplicita in qualsiasi costruttore di container. – Potatoswatter

+0

VS 2010 Beta si comporta come Comeau e gcc. –

risposta

3

Ho esaminato l'implementazione STL di GCC e dovrebbe avere un comportamento simile. Ecco perché.

  • elementi di un vector vengono inizializzati da un modello di funzione generica che accetta qualsiasi due tipi X e V e chiama new(p) X(v) dove v è una V (sto parafrasando un po '). Ciò consente la conversione esplicita.
  • Elementi di un set o map vengono inizializzati da una funzione membro privata di _tree<T,…> che aspetta specificamente un T const & da passare. Questa funzione membro non è un modello (oltre ad essere un membro di un modello), per cui se l'iniziale il valore non può essere convertito implicitamente in T, la chiamata non riesce. (Sto ancora semplificando il codice.)

Lo standard non richiede che il lavoro di conversione esplicita o quella conversione implicita non funzioni durante l'inizializzazione di un contenitore con un intervallo. Dice semplicemente che l'intervallo viene copiato nel contenitore. Decisamente ambiguo per il tuo scopo.

Sorprendente tale ambiguità esiste, considerando come hanno già perfezionato lo standard in considerazione di problemi come the one I had un paio di settimane fa.

1

Penso che dipenderebbe dal modo in cui std::vector<A> As(Iterator,Iterator) è implementato nell'implementazione particolare dell'STL.

+0

Il comportamento della libreria standard non dovrebbe dipendere dall'implementazione. –

+1

Poi di nuovo, ci sono luoghi (molto rari) in cui lo standard può essere interpretato in due modi non equivalenti. –

0

Questo codice non viene compilato in Comeau: messaggio

class Foo 
{ 
public: 
explicit Foo(int bar) 
{ 
} 
}; 

class Bar 
{ 
void DoStuff(Foo foo){ 

} 
void DoStuff2() 
{ 
    DoStuff(4); 
} 
}; 

Errore:

"ComeauTest.c", line 16: error: no suitable constructor exists to convert from "int" 
      to "Foo" 
    DoStuff(4); 
      ^

1 error detected in the compilation of "ComeauTest.c". 

Quindi a livello rudimentale il compilatore online supporta costruttori espliciti. Deve essere qualcosa a che fare con il vettore/iteratori.

EDIT Questo però compila:

Foo foo = (Foo)5; 

che è una conversione esplicita, così va bene. Suppongo che la classe vettoriale Comeau faccia un cast esplicito nel costruttore da qualche parte, dove non sia la libreria di Microsoft.

Maggiori info su costruttori espliciti - http://www.glenmccl.com/tip_023.htm

0

Si deve compilare. Se il costruttore non viene utilizzato, il suo esplicito non è un problema.

+0

Se il costruttore non viene utilizzato, in quale altro modo stiamo convertendo da un int a un A? – user200783

+0

Nulla viene creato e non viene convertito nulla - entrambi i vettori sono vuoti –

+0

Il comportamento è lo stesso se qualcosa viene inserito nel vettore "int" prima della costruzione di "Come". – user200783

1

Questa è una domanda piuttosto complicata, e potrebbe essere il caso che VisualStudio abbia ragione e Comeau torto (questo sembra davvero difficile da credere).

Lo standard se parola letta per parola, definisce tale costruttore vettore in termini di costruttore di copia (vedi citazione), e ciò significa letteralmente che l'oggetto ottenuto dereferenziando all'iteratore deve essere prima convertito nel tipo T e quindi si dovrebbe chiamare il costruttore di copia. A questo punto, con un costruttore esplicito il codice non dovrebbe essere compilato.

Sembra ragionevole prevedere un'implementazione, al contrario, chiamare direttamente un costruttore che prenda come argomento l'iteratore dereferenziato, nel qual caso la chiamata del costruttore sarebbe esplicita e quindi il codice dovrebbe essere compilato.Ciò andrebbe contro l'esatta formulazione nella citazione di seguito, come il costruttore copia è definita per un dato tipo T come un costruttore che prendendo un unico riferimento eventualmente costante ad un oggetto di tipo T.

Non posso pensare ogni ragionevole argomento per non usare l'approccio di Comeau, e credo (questa è solo un'opinione personale) è che la formulazione nello standard rispetto alla complessità del costruttore di vettore dovrebbe probabilmente essere riformulata come richiesto solo N chiamate all'appropriato T costruttore, se del caso dovrebbe essere definito come il costruttore che corrisponde alla chiamata T(*first) (ovvero, un costruttore che prende un InputIterator::value_type (in base al valore o eventualmente un riferimento costante) o il const di copia T ructor dopo una conversione implicita da InputIterator::value_type a T.

23.2.4.1 [lib.vector.cons]/1

Complessità: Il modello costruttore vettoriale (InputIterator primo, InputIterator ultimo) rende solo N chiama il costruttore di copia di T (dove N è la distanza tra prima e l'ultima) e nessuna riallocazione se gli iteratori primo e ultimo sono di di accesso, bidirezionale o casuale categorie di accesso. Effettua le chiamate N al costruttore di copie di T e riassegnazioni del registro degli ordini se sono solo input iteratori.

vorrei sapere come si comporta quando dato il compilatore VS:

struct T1; 
struct T2 { 
    operator T1(); 
}; 
struct T1 { 
    T1(T2 const &) { std::cout << "T1(T2)" << std::endl; } 
}; 
T2::operator T1() { 
    std::cout << "T2::operator T1" << std::endl; 
    return T1(*this); 
} 
int main() { 
    std::vector<T2> v2; 
    v2.push_back(T2()); 
    std::vector<T1> v1(v2.begin(), v2.end()); 
} 

Con g ++ il risultato è che T2::operator T1 non si chiama, ma gli elementi in v1 sono costruiti direttamente dagli elementi in v2. Suppongo che con VS il compilatore utilizzi T2::operator T1 per convertire da ogni elemento in v2 a un elemento T1 e quindi chiamare il costruttore di copie. È così?

+0

Infatti, provare a compilare il codice con il compilatore VS dà un errore (C2664): 'std :: allocator <_Ty> :: construct': impossibile convertire il parametro 2 da 'T2' a 'const T1 &' con [_Ty = T1 ] Motivo: impossibile convertire da 'T2' a 'const T1' Nessun operatore di conversione definito dall'utente disponibile che può eseguire questa conversione, oppure l'operatore non può essere chiamato – user200783

+0

Ho appena testato con VS2010 beta e non solo compila ma esegue con lo stesso comportamento di g ++: viene chiamato il costruttore 'T1 (T2 const &)'. –

1

Si tratta in realtà di una questione di come viene implementata la libreria STL, non un problema di specifica del linguaggio. Non c'è nulla nelle specifiche del linguaggio che vieterebbe a questo di funzionare, né c'è nulla che richiederebbe che funzioni.

Se il costruttore stl :: vector è stato scritto per provare una conversione implicita utilizzando l'operatore di assegnazione, non funzionerà. È più probabile che l'implementazione Microsoft STL utilizzi l'ottimizzazione del valore di ritorno durante l'inizializzazione tramite una chiamata del costruttore, nel qual caso questo codice funzionerebbe correttamente.

È importante notare che l'unica ragione per cui questo funziona è perché il costruttore stl :: vector è basato su modelli, e l'unico requisito è che sia un input_iterator, o più precisamente che supporti tutte le funzionalità richieste di un input iteratore.

Vorrei anche sottolineare che questo è un primo esempio del perché è spesso difficile scrivere codice multipiattaforma. A volte si finisce con problemi in cui nessuno dei compilatori necessariamente devia dallo standard del linguaggio, ma il codice non è ancora portatile.