2013-02-16 18 views
10

Zapraszamy do obejrzenia poniższego kodu:std :: map <> :: insert przy użyciu non-copyable obiektów i jednolitej inicjalizacji

#include <utility> 
#include <map> 

// non-copyable but movable 
struct non_copyable { 
    non_copyable() = default; 

    non_copyable(non_copyable&&) = default; 
    non_copyable& operator=(non_copyable&&) = default; 

    // you shall not copy 
    non_copyable(const non_copyable&) = delete; 
    non_copyable& operator=(const non_copyable&) = delete; 
}; 

int main() { 
    std::map<int, non_copyable> map; 
    //map.insert({ 1, non_copyable() }); < FAILS 
    map.insert(std::make_pair(1, non_copyable())); 
    //^same and works 
} 

Kompilacja ten fragment nie powiedzie się podczas odkomentowanie zaznaczoną linię na g ++ 4.7. Utworzony błąd wskazuje, że non_copyable nie można skopiować, ale oczekiwałem, że zostanie przeniesiony.

Dlaczego wstawianie std::pair skonstruowanego przy użyciu jednolitej inicjalizacji kończy się niepowodzeniem, ale nie zostało wykonane przy użyciu std::make_pair? Czy obie strony nie powinny produkować wartości rublicznych, które można z powodzeniem przenieść na mapę?

Odpowiedz

17

[To jest kompletny przepis. Moja odpowiedź wcześniej nie miał nic wspólnego z tym problemem]

map ma dwie istotne insert przeciążeń.

  • insert(const value_type& value) i

  • <template typename P> insert(P&& value).

Podczas korzystania z prostego inicjatora listy map.insert({1, non_copyable()}); uwzględniane są wszystkie możliwe przeciążenia. Ale tylko pierwszy (ten, który przyjmuje const value_type&) został znaleziony, ponieważ drugi nie ma sensu (nie ma sposobu, aby magicznie odgadnąć, że masz zamiar stworzyć parę). Pierwsze obciążenie ponad ­ nie działa, ponieważ twój element nie może być kopiowany.

można dokonać drugą pracę przeciążeniem tworząc parę wyraźnie, albo z make_pair, jak już opisano, albo nazywając typ wartości extenso:

typedef std::map<int, non_copyable> map_type; 

map_type m; 
m.insert(map_type::value_type({1, non_copyable()})); 

Teraz lista-inicjator wie szukać map_type::value_type konstruktorów, znajdzie odpowiednią mova ­ ble, a wynikiem jest para rvalue, która wiąże się z P&& -wystąpienie funkcji insert.

(Inną opcją jest użycie emplace() z piecewise_construct i forward_as_tuple, mimo że dostanie dużo bardziej gadatliwy.)

Przypuszczam tutaj moralne jest to, że list-inicjalizatory szukać żywych przeciążeń – ale muszą wiedzieć czego szukać!

+1

Tak, to w zasadzie to, co napisałem przed usunięciem mojej odpowiedzi. Mam jednak wątpliwości: dlaczego utworzono tutaj 'initializer_list <>? 'std :: pair ' nie ma konstruktora, który je posiada. Myślałem, że jednolita składnia inicjalizacji wybierze zwykły konstruktor 'pair <>'. –

+1

Również elementy initializer_list <> muszą być tego samego typu, nie? – eladidan

+0

+1 do obu komentarzy. W moim przykładzie nie powinno być nawet 'initializer_list'. To bardziej przypomina wywołanie konstruktora 'std :: pair' przy użyciu jednolitej inicjalizacji. – mfontanini