Jest dobrze znany obraz (ściągawka) o nazwie „Wybór kontenera w C ++”. Jest to schemat blokowy wyboru najlepszego pojemnika do zamierzonego zastosowania.
Czy ktoś wie, czy istnieje już wersja C ++ 11 tego?
To jest poprzednie:

Jest dobrze znany obraz (ściągawka) o nazwie „Wybór kontenera w C ++”. Jest to schemat blokowy wyboru najlepszego pojemnika do zamierzonego zastosowania.
Czy ktoś wie, czy istnieje już wersja C ++ 11 tego?
To jest poprzednie:

Odpowiedzi:
Nie żebym o tym wiedział, ale myślę, że można to zrobić tekstowo. Poza tym wykres jest nieco zepsuty, ponieważ listw ogóle nie jest tak dobrym pojemnikiem, ani nie jest forward_list. Obie listy są bardzo wyspecjalizowanymi kontenerami do zastosowań niszowych.
Aby zbudować taki wykres, potrzebujesz tylko dwóch prostych wskazówek:
Martwienie się o wydajność jest zwykle na początku bezużyteczne. Wielkie rozważania dotyczące O pojawiają się dopiero wtedy, gdy zaczniesz zajmować się kilkoma tysiącami (lub więcej) przedmiotów.
Istnieją dwie duże kategorie kontenerów:
findoperacjęa następnie można zbudować kilka adapterów na nich: stack, queue, priority_queue. Zostawię tutaj adaptery, są wystarczająco wyspecjalizowane, aby można je było rozpoznać.
Pytanie 1: Stowarzyszenie ?
Pytanie 1.1: Zamówione ?
unordered_kontenera, w przeciwnym razie skorzystaj z jego tradycyjnie zamówionego odpowiednika.Pytanie 1.2: Oddzielny klucz ?
map, w przeciwnym razie użyj asetPytanie 1.3: Duplikaty ?
multi, w przeciwnym razie nie rób tego.Przykład:
Załóżmy, że mam kilka osób z przypisanym do nich unikalnym identyfikatorem i chciałbym uzyskać dane osoby z jego identyfikatora tak prosto, jak to możliwe.
Chcę findfunkcji, a więc kontenera asocjacyjnego
1.1. Nie mogłem mniej przejmować się porządkiem, więc unordered_kontenerem
1.2. Mój klucz (ID) jest niezależny od wartości, z którą jest powiązany, a zatem amap
1.3. Identyfikator jest unikalny, więc żaden duplikat nie powinien się wkradać.
Ostateczna odpowiedź brzmi: std::unordered_map<ID, PersonData>.
Pytanie 2: Pamięć stabilna ?
listPytanie 2.1: Które ?
list; a forward_listjest przydatna tylko w przypadku mniejszego zużycia pamięci.Pytanie 3: Rozmiar dynamiczny ?
{ ... }składni), użyj array. Zastępuje tradycyjną tablicę C, ale oferuje wygodne funkcje.Pytanie 4: Podwójnie zakończone ?
deque, w przeciwnym razie użyj vector.Zauważysz, że domyślnie, jeśli nie potrzebujesz kontenera asocjacyjnego, Twoim wyborem będzie plik vector. Okazuje się, że jest to również rekomendacja Suttera i Stroustrupa .
arraynie wymaga domyślnego typu konstrukcyjnego; 2) wybieranie multis to nie tyle kwestia pozwolenia na duplikaty, ile raczej to, czy ich przechowywanie ma znaczenie (duplikaty można włożyć do innych niż multipojemniki, tak się składa, że jest tylko jeden).
map.find(key)jest dużo przyjemniejszy niż std::find(map.begin(), map.end(), [&](decltype(map.front()) p) { return p.first < key; }));chociażby, dlatego ważne jest semantycznie, że findjest to funkcja składowa, a nie ta z <algorithm>. Jeśli chodzi o O (1) vs O (log n), nie wpływa to na semantykę; Usunę „wydajnie” z przykładu i zastąpię „łatwo”.
dequema tę właściwość?
dequeelementy są stabilne tylko wtedy, gdy wepchniesz / pop na każdym końcu; jeśli zaczniesz wstawiać / wymazywać w środku, tasuje się do N / 2 elementów, aby wypełnić powstałą lukę.
Podoba mi się odpowiedź Matthieu, ale zamierzam powtórzyć schemat blokowy w następujący sposób:
Domyślnie, jeśli potrzebujesz pojemnika z rzeczami, użyj std::vector. Zatem każdy inny kontener jest uzasadniony tylko przez zapewnienie jakiejś funkcjonalności alternatywnej dla std::vector.
std::vectorwymaga, aby jego zawartość była możliwa do zbudowania, ponieważ musi mieć możliwość tasowania przedmiotów wokół. Nie jest to strasznym obciążeniem dla zawartości (pamiętaj, że domyślne konstruktory nie są wymagane , dzięki emplaceitd.). Jednak większość pozostałych kontenerów nie wymaga żadnego konkretnego konstruktora (ponownie dzięki emplace). Więc jeśli masz obiekt, w którym absolutnie nie możesz zaimplementować konstruktora przenoszenia, będziesz musiał wybrać coś innego.
A std::dequebyłby ogólnym zamiennikiem, mającym wiele właściwości std::vector, ale można wstawić tylko na obu końcach deque. Wkładki w środku wymagają przesuwania. A std::listnie nakłada żadnych wymagań na zawartość.
std::vector<bool>nie jest. Cóż, to standard. Ale nie jest to vectorw zwykłym sensie, ponieważ operacje, które std::vectorzwykle na to pozwalają, są zabronione. I z całą pewnością nie zawiera bools .
Dlatego jeśli potrzebujesz prawdziwego vectorzachowania z pojemnika bools, nie uzyskasz go z std::vector<bool>. Więc będziesz musiał spłacić std::deque<bool>.
Jeśli potrzebujesz znaleźć elementy w kontenerze, a tag wyszukiwania nie może być tylko indeksem, być może będziesz musiał porzucić std::vectorna korzyść seti map. Zwróć uwagę na słowo kluczowe „ może ”; posortowany std::vectorjest czasem rozsądną alternatywą. Lub Boost.Container's flat_set/map, który implementuje sortowany plik std::vector.
Obecnie istnieją cztery ich odmiany, każda z własnymi potrzebami.
mapgdy tag wyszukiwania nie jest tym samym, co przedmiot, którego szukasz. W przeciwnym razie użyj pliku set.unorderedgdy masz dużo elementów w kontenerze i wydajność wyszukiwania absolutnie musi być O(1), a nie O(logn).multijeśli potrzebujesz wielu elementów, które mają ten sam tag wyszukiwania.Jeśli chcesz, aby kontener elementów był zawsze sortowany na podstawie określonej operacji porównania, możesz użyć pliku set. Lub multi_setjeśli chcesz, aby wiele elementów miało tę samą wartość.
Lub możesz użyć posortowanego std::vector, ale będziesz musiał to posortować.
Czasami problemem jest unieważnienie iteratorów i odwołań. Jeśli potrzebujesz listy elementów, takiej, że masz iteratory / wskaźniki do tych elementów w różnych innych miejscach, to std::vectorpodejście do unieważniania może nie być odpowiednie. Każda operacja wstawiania może spowodować unieważnienie, w zależności od bieżącego rozmiaru i pojemności.
std::listoferuje solidną gwarancję: iterator i powiązane z nim odniesienia / wskaźniki są unieważniane tylko wtedy, gdy sam element zostanie usunięty z kontenera. std::forward_listjest, jeśli pamięć jest poważnym problemem.
Jeśli jest to zbyt mocna gwarancja, std::dequeoferuje słabszą, ale użyteczną gwarancję. Unieważnienie wynika z wstawień w środku, ale wstawienia na początku lub końcu powoduje unieważnienie tylko iteratorów , a nie wskaźników / odwołań do elementów w kontenerze.
std::vector zapewnia tylko tanie wkładanie na końcu (a nawet wtedy staje się drogie, jeśli wydmuchujesz moc).
std::listjest drogi pod względem wydajności (każdy nowo wstawiony element kosztuje alokację pamięci), ale jest spójny . Oferuje również czasami niezbędną możliwość tasowania przedmiotów praktycznie bez kosztów wydajności, a także wymiany przedmiotów z innymi std::listpojemnikami tego samego typu bez utraty wydajności. Jeśli potrzebujesz dużo tasować rzeczy , użyj std::list.
std::dequezapewnia ciągłe wkładanie / wyjmowanie z głowy i ogona, ale wprowadzenie w środku może być dość kosztowne. Więc jeśli chcesz dodać / usunąć rzeczy z przodu, jak iz tyłu, std::dequemoże być tym, czego potrzebujesz.
Należy zauważyć, że dzięki semantyce przenoszenia std::vectorwydajność wstawiania może nie być tak zła, jak kiedyś. Niektóre implementacje implementowały formę opartego na semantyce przenoszenia elementów (tzw. „Swaptimizacja”), ale teraz, gdy przenoszenie jest częścią języka, jest narzucone przez standard.
std::arrayjest dobrym kontenerem, jeśli chcesz mieć jak najmniej alokacji dynamicznych. To tylko opakowanie wokół tablicy C; oznacza to, że jego rozmiar musi być znany w czasie kompilacji . Jeśli możesz z tym żyć, użyj std::array.
Biorąc to pod uwagę, używanie std::vectori określanie reserverozmiaru będzie działać równie dobrze w przypadku ograniczonego std::vector. W ten sposób rzeczywisty rozmiar może się różnić i masz tylko jedną alokację pamięci (chyba że wydmuchasz pojemność).
std::sorttego jest też std::inplace_mergeco jest interesujące, aby łatwo umieścić nowe elementy (zamiast std::lower_bound+ std::vector::insertwywołania). Miło się dowiedzieć flat_seti flat_map!
vector<bool>jest vector<char>.
std::allocator<T>nie obsługuje tego wyrównania (i nie wiem, dlaczego nie), zawsze możesz użyć własnego niestandardowego alokatora.
std::vector::resizema przeciążenie, które nie przyjmuje wartości (po prostu przyjmuje nowy rozmiar; wszelkie nowe elementy będą domyślnie konstruowane w miejscu). Ponadto, dlaczego kompilatory nie są w stanie poprawnie wyrównać parametrów wartości, nawet jeśli zadeklarowano, że mają to wyrównanie?
bitsetdla bool, jeśli znasz rozmiar z góry en.cppreference.com/w/cpp/utility/bitset
Oto wersja powyższego schematu blokowego w języku C ++ 11. [pierwotnie opublikowany bez podawania autora, Mikaela Perssona ]

Oto szybki obrót, chociaż prawdopodobnie wymaga pracy
Should the container let you manage the order of the elements?
Yes:
Will the container contain always exactly the same number of elements?
Yes:
Does the container need a fast move operator?
Yes: std::vector
No: std::array
No:
Do you absolutely need stable iterators? (be certain!)
Yes: boost::stable_vector (as a last case fallback, std::list)
No:
Do inserts happen only at the ends?
Yes: std::deque
No: std::vector
No:
Are keys associated with Values?
Yes:
Do the keys need to be sorted?
Yes:
Are there more than one value per key?
Yes: boost::flat_map (as a last case fallback, std::map)
No: boost::flat_multimap (as a last case fallback, std::map)
No:
Are there more than one value per key?
Yes: std::unordered_multimap
No: std::unordered_map
No:
Are elements read then removed in a certain order?
Yes:
Order is:
Ordered by element: std::priority_queue
First in First out: std::queue
First in Last out: std::stack
Other: Custom based on std::vector?????
No:
Should the elements be sorted by value?
Yes: boost::flat_set
No: std::vector
Możesz zauważyć, że różni się to znacznie od wersji C ++ 03, głównie ze względu na fakt, że naprawdę nie lubię połączonych węzłów. Połączone kontenery węzłów mogą zwykle być lepsze od wydajności niepołączonego kontenera, z wyjątkiem kilku rzadkich sytuacji. Jeśli nie wiesz, jakie są te sytuacje i masz dostęp do przyspieszenia, nie używaj kontenerów połączonych węzłów. (std :: list, std :: slist, std :: map, std :: multimap, std :: set, std :: multiset). Ta lista skupia się głównie na kontenerach o małych i średnich ścianach, ponieważ (A) to 99,99% tego, co mamy do czynienia w kodzie, a (B) Duża liczba elementów wymaga niestandardowych algorytmów, a nie różnych kontenerów.