web-dev-qa-db-fra.com

Pourquoi l'argument du type de carte C ++ nécessite-t-il un constructeur vide lors de l'utilisation de []?

Voir aussi liste standard C++ et types constructibles par défaut

Pas un problème majeur, juste ennuyeux car je ne veux pas que ma classe soit instanciée sans les arguments particuliers.

#include <map>

struct MyClass
{
    MyClass(int t);
};

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

Cela me donne l'erreur g ++ suivante:

/usr/include/c++/4.3/bits/stl_map.h:419: erreur: pas de fonction correspondante pour l'appel à ‘MyClass ()’

Cela compile bien si j'ajoute un constructeur par défaut; Je suis certain que cela n'est pas dû à une syntaxe incorrecte.

85
Nick Bolton

Ce problème vient avec l'opérateur []. Citation de la documentation SGI:

data_type& operator[](const key_type& k) - Renvoie une référence à l'objet associé à une clé particulière. Si la carte ne contient pas déjà un tel objet, operator[] Insère l'objet par défaut data_type().

Si vous n'avez pas de constructeur par défaut, vous pouvez utiliser les fonctions d'insertion/recherche. L'exemple suivant fonctionne bien:

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

Oui. Les valeurs des conteneurs STL doivent conserver la sémantique de copie. IOW, ils doivent se comporter comme des types primitifs (par exemple int), ce qui signifie, entre autres, qu'ils doivent être constructibles par défaut.

Sans cela (et d'autres exigences), il serait inutilement difficile d'implémenter les diverses opérations internes de copie/déplacement/échange/comparaison sur les structures de données avec lesquelles les conteneurs STL sont implémentés.

En référence à la norme C++, je vois que ma réponse n'était pas exacte. La construction par défaut n'est en fait pas une exigence :

À partir du 20.1.4.1:

Le constructeur par défaut n'est pas requis. Certaines signatures de fonction membre de classe de conteneur spécifient le constructeur par défaut comme argument par défaut. T() doit être une expression bien définie ...

Donc, à strictement parler, votre type de valeur n'a besoin d'être constructible par défaut que si vous utilisez une fonction du conteneur qui utilise le constructeur par défaut dans sa signature.

Les exigences réelles (23.1.3) de toutes les valeurs stockées dans les conteneurs STL sont CopyConstructible et Assignable.

Il existe également d'autres exigences spécifiques pour des conteneurs particuliers, comme être Comparable (par exemple pour les clés dans une carte).


Par ailleurs, ce qui suit se compile sans erreur sur comea :

#include <map>

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

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

Cela pourrait donc être un problème g ++.

6
Assaf Lavie

Vérifiez les exigences du type stocké de la stl :: map. De nombreuses collections stl nécessitent que le type stocké contienne certaines propriétés spécifiques (constructeur par défaut, constructeur de copie, etc.).

Le constructeur sans arguments est nécessaire à stl :: map, car il est utilisé lorsque l'opérateur [] est invoqué avec la clé, qui n'a pas déjà été conservée par la carte. Dans ce cas, l'opérateur [] insère la nouvelle entrée constituée de la nouvelle clé et de la nouvelle valeur construites à l'aide du constructeur sans paramètre. Et cette nouvelle valeur est ensuite renvoyée.

2
oo_olo_oo