Dlaczego argument typu mapy C ++ wymaga pustego konstruktora podczas używania []?


99

Zobacz także standardowa lista C ++ i typy konstruowane domyślnie

Nie jest to poważny problem, po prostu denerwujący, ponieważ nie chcę, aby moja klasa była kiedykolwiek tworzona bez określonych argumentów.

#include <map>

struct MyClass
{
    MyClass(int t);
};

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

To daje mi następujący błąd g ++:

/usr/include/c++/4.3/bits/stl_map.h:419: błąd: brak funkcji dopasowującej dla wywołania „MyClass ()”

Kompiluje się dobrze, jeśli dodam domyślny konstruktor; Jestem pewien, że nie jest to spowodowane nieprawidłową składnią.


Powyższy kod dobrze kompiluje się na MinGW (g ++ 3.4.5) i MSVC ++ 2008, pod warunkiem, że zostanie podana nazwa typu MyType i średnik na końcu klasy. Musisz robić coś innego (np. Dzwoniąc do operatora [], o czym wspomniał bb) - prześlij pełny kod.
j_random_hacker

Ach, tak, masz rację. Zrobi się.
Nick Bolton,

Tak, bez użycia myMap nie wiesz, co należy skompilować dla klasy mapy. Który dostawca biblioteki stl i wersja też mogą pomóc.
Greg Domjan

Odpowiedzi:


165

Ten problem dotyczy operatora []. Cytat z dokumentacji SGI:

data_type& operator[](const key_type& k)- Zwraca odniesienie do obiektu, który jest skojarzony z określonym kluczem. Jeśli mapa nie zawiera jeszcze takiego obiektu, operator[] wstawia obiekt domyślny data_type().

Jeśli nie masz domyślnego konstruktora, możesz użyć funkcji wstawiania / znajdowania. Poniższy przykład działa dobrze:

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

11
Doskonała odpowiedź - zauważ również emplacew C ++ 11 jako zwięzłą alternatywę dla insert.
Prideout

3
Dlaczego jest to zawarte std::<map>::value_typew insertwezwaniu?
thomthom

1
Dlaczego domyślny konstruktor musi być zdefiniowany przez użytkownika?
schuess

@schuess Nie widzę powodu, dla którego to działa: = defaultpowinno działać dobrze.
underscore_d

Warunek „Mapa nie zawiera już takiego obiektu” zostałby oceniony w czasie wykonywania. Dlaczego błąd czasu kompilacji?
Gaurav Singh

7

Tak. Wartości w kontenerach STL muszą zachowywać semantykę kopiowania. IOW, muszą zachowywać się jak typy pierwotne (np. Int), co oznacza między innymi, że powinny być domyślnie konstruowane.

Bez tego (i innych wymagań) niepotrzebnie trudno byłoby zaimplementować różne wewnętrzne operacje kopiowania / przenoszenia / zamiany / porównania na strukturach danych, z którymi zaimplementowano kontenery STL.

Po odwołaniu się do standardu C ++ widzę, że moja odpowiedź nie była dokładna. Konstrukcja domyślna nie jest w rzeczywistości wymogiem :

Od 20.1.4.1:

Domyślny konstruktor nie jest wymagany. Niektóre podpisy funkcji składowej klasy kontenera określają domyślny konstruktor jako argument domyślny. T () musi być dobrze zdefiniowanym wyrażeniem ...

Tak więc, mówiąc ściśle, twój typ wartości musi być domyślnym konstruowalnym tylko wtedy, gdy używasz funkcji kontenera, która używa domyślnego konstruktora w swoim podpisie.

Rzeczywiste wymagania (23.1.3) ze wszystkich wartości przechowywanych w kontenerach STL to CopyConstructiblei Assignable.

Istnieją również inne specyficzne wymagania dla poszczególnych kontenerów, takie jak istnienie Comparable(np. Dla kluczy na mapie).


Nawiasem mówiąc, następujące kompilacje bez błędu na comeau :

#include <map>

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

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

Więc może to być problem g ++.


2
Czy myślisz, że bb może mieć coś na temat operatora []?
Nick Bolton,

13
Ten kod prawdopodobnie kompiluje się, ponieważ nie wywołujesz myMap []
jfritz42

3

Sprawdź wymagania dotyczące przechowywanego typu stl :: map. Wiele kolekcji stl wymaga, aby typ przechowywany zawierał określone właściwości (domyślny konstruktor, konstruktor kopiujący itp.).

Konstruktor bez argumentów jest potrzebny w stl :: map, ponieważ jest używany, gdy operator [] jest wywoływany z kluczem, który nie został jeszcze zachowany przez mapę. W tym przypadku operator [] wstawia nowy wpis składający się z nowego klucza i wartości skonstruowanych przy użyciu konstruktora bez parametrów. I ta nowa wartość jest następnie zwracana.


-2

Sprawdź czy:

  • Zapomniałeś ';' po deklaracji klasy.
  • MyType powinien zostać odpowiednio zadeklarowany.
  • Brak domyślnego konstruktora ...

Wydaje mi się, że deklaracja std :: map wydaje się poprawna.


Kompiluje się dobrze, jeśli dodam domyślny konstruktor.
Nick Bolton,

-2

Najprawdopodobniej wymaga tego std :: pair. std :: pair przechowuje dwie wartości przy użyciu semantyki wartości, więc musisz mieć możliwość ich wystąpienia bez parametrów. Tak więc kod używa std :: pair w różnych miejscach, aby zwrócić wartości mapy do obiektu wywołującego i jest to zwykle robione przez utworzenie wystąpienia pustej pary i przypisanie do niej wartości przed zwróceniem pary lokalnej.

Możesz obejść ten problem za pomocą inteligentnych wskaźników, używając map <int, smartptr <MyClass>>, ale to zwiększa obciążenie związane ze sprawdzaniem pustych wskaźników.


2
+0. pair <T, U> może być użyte dobrze z typami T i U pozbawionymi domyślnych konstruktorów - jedyną rzeczą, której nie można użyć w tym przypadku, jest własny domyślny konstruktor pair <T, U>. Żadna implementacja mapy <K, V> przyzwoitej jakości nie użyłaby tego domyślnego konstruktora, ponieważ ogranicza to, jakie mogą być K i V.
j_random_hacker
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.