Perché l'argomento del tipo di mappa C ++ richiede un costruttore vuoto quando si usa []?


98

Vedi anche elenco standard C ++ e tipi costruibili di default

Non è un grosso problema, è solo fastidioso perché non voglio che la mia classe venga mai istanziata senza argomenti particolari.

#include <map>

struct MyClass
{
    MyClass(int t);
};

int main() {
    std::map<int, MyClass> myMap;
    myMap[14] = MyClass(42);
}

Questo mi dà il seguente errore g ++:

/usr/include/c++/4.3/bits/stl_map.h:419: errore: nessuna funzione corrispondente per la chiamata a "MyClass ()"

Questo si compila bene se aggiungo un costruttore predefinito; Sono certo che non sia causato da una sintassi errata.


Il codice sopra si compila perfettamente su MinGW (g ++ 3.4.5) e MSVC ++ 2008, a condizione che venga fornito un typedef per MyType e un punto e virgola aggiunto alla fine della classe. Devi fare qualcos'altro (ad esempio chiamare l'operatore [] come menzionato da bb) - inserisci il codice completo .
j_random_hacker

Ah, sì, hai ragione. Andrà bene.
Nick Bolton

Sì, senza l'utilizzo di myMap non sai cosa deve essere compilato per la classe map. Anche quale provider di libreria STL e versione potrebbe essere d'aiuto.
Greg Domjan

Risposte:


165

Questo problema viene fornito con l'operatore []. Citazione dalla documentazione SGI:

data_type& operator[](const key_type& k)- Restituisce un riferimento all'oggetto associato a una particolare chiave. Se la mappa non contiene già un tale oggetto, operator[] inserisce l'oggetto predefinito data_type().

Se non hai un costruttore predefinito puoi usare le funzioni di inserimento / ricerca. Il seguente esempio funziona bene:

myMap.insert( std::map< int, MyClass >::value_type ( 1, MyClass(1) ) );
myMap.find( 1 )->second;

11
Risposta eccellente: nota anche emplacein C ++ 11 come alternativa concisa a insert.
prideout

3
Perché è std::<map>::value_typepresente nella insertchiamata?
domani

1
Perché il costruttore predefinito deve essere definito dall'utente?
schuess

@schuess Non vedo motivo per cui funziona: = defaultdovrebbe funzionare bene.
underscore_d

La condizione "La mappa non contiene già tale oggetto" verrà valutata in fase di esecuzione. Perché un errore in fase di compilazione?
Gaurav Singh,

8

Sì. I valori nei contenitori STL devono mantenere la semantica della copia. IOW, devono comportarsi come tipi primitivi (ad esempio int) il che significa, tra le altre cose, che dovrebbero essere costruibili in modo predefinito.

Senza questo (e altri requisiti) sarebbe inutilmente difficile implementare le varie operazioni interne di copia / spostamento / scambio / confronto sulle strutture dati con cui sono implementati i contenitori STL.

In riferimento allo standard C ++, vedo che la mia risposta non era accurata. La costruzione predefinita, infatti, non è un requisito :

Dalla 20.1.4.1:

Il costruttore predefinito non è richiesto. Alcune firme della funzione membro della classe contenitore specificano il costruttore predefinito come argomento predefinito. T () deve essere un'espressione ben definita ...

Quindi, in senso stretto, il tipo di valore deve essere costruibile di default solo se ti capita di utilizzare una funzione del contenitore che utilizza il costruttore di default nella sua firma.

I requisiti reali (23.1.3) da tutti i valori memorizzati nei contenitori STL sono CopyConstructiblee Assignable.

Ci sono anche altri requisiti specifici per contenitori particolari, come l'essere Comparable(ad esempio per le chiavi in ​​una mappa).


Per inciso, il seguente viene compilato senza errori su comeau :

#include <map>

class MyClass
{
public:
    MyClass(int t);
};

int main()
{
    std::map<int, MyClass> myMap;
}

Quindi questo potrebbe essere un problema di g ++.


2
Pensi che bb potrebbe avere a che fare con qualcosa che riguarda l'operatore []?
Nick Bolton

12
Quel codice probabilmente viene compilato perché non stai chiamando myMap []
jfritz42

3

Verificare i requisiti del tipo memorizzato di stl :: map. Molte raccolte stl richiedono che il tipo archiviato contenga alcune proprietà specifiche (costruttore predefinito, costruttore di copia, ecc.).

Il costruttore senza argomenti è necessario per stl :: map, perché viene utilizzato quando l'operatore [] viene invocato con la chiave, che non è già stato mantenuto dalla mappa. In questo caso l'operatore [] inserisce la nuova voce costituita dalla nuova chiave e valore costruiti utilizzando un costruttore senza parametri. E questo nuovo valore viene quindi restituito.


-2

Controlla se:

  • Hai dimenticato il carattere ";" dopo la dichiarazione di classe.
  • MyType avrebbe dovuto essere dichiarato di conseguenza.
  • Nessun costruttore predefinito lì ...

La dichiarazione std :: map sembra corretta, credo.


Compila bene se aggiungo un costruttore predefinito.
Nick Bolton

-2

Molto probabilmente perché std :: pair lo richiede. std :: pair contiene due valori utilizzando la semantica dei valori, quindi è necessario essere in grado di istanziarli senza parametri. Quindi il codice utilizza std :: pair in vari punti per restituire i valori della mappa al chiamante e questo viene comunemente fatto istanziando una coppia vuota e assegnandovi i valori prima di restituire la coppia locale.

È possibile aggirare questo problema con i puntatori intelligenti utilizzando una mappa <int, smartptr <MyClass>> ma ciò aggiunge l'overhead del controllo dei puntatori nulli.


2
+0. pair <T, U> può essere usato bene con i tipi T e U privi di costruttori predefiniti - l'unica cosa che non può essere usata in questo caso è il costruttore predefinito di pair <T, U>. Nessuna implementazione di qualità decente di map <K, V> userebbe questo costruttore predefinito perché limita ciò che K e V possono essere.
j_random_hacker
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.