Pageidaujamas / idiomatinis būdas įterpti į kortelę

Nustatiau keturis skirtingus būdus, kaip įterpti į std::map :

 std::map<int, int> function; function[0] = 42; function.insert(std::map<int, int>::value_type(0, 42)); function.insert(std::pair<int, int>(0, 42)); function.insert(std::make_pair(0, 42)); 

Kuris iš jų yra tinkamiausias / idiomatinis būdas? (Ir ar dar ne taip galvojau?)

72
26 нояб. nustatyta fredoverflow 26 nov. 2010-11-26 18:48 '10, 18:48, 2010-11-26 18:48
@ 8 atsakymai

Visų pirma, narių funkcijų operator[] ir insert nėra funkciniu požiūriu lygiaverčiai:

  • operator[] ieškos rakto, įterpti numatytąją vertę, jei ji nerastų, ir grąžinkite nuorodą, su kuria priskiriate vertę. Akivaizdu, kad tai gali būti neveiksminga, jei mapped_type gali būti naudingas tiesioginiam inicijavimui, o ne numatytam ir priskirtam. Šis metodas taip pat neleidžia nustatyti, ar įterptas įrašas, ar tiesiog perrašėte anksčiau įterpto rakto vertę.
  • insert nario funkcija neturės jokios įtakos, jei raktas jau yra žemėlapyje ir, nors jis dažnai yra pamirštas, grąžina std::pair<iterator, bool> , kuris gali būti įdomus (svarbiausia, nustatyti, ar įterpimas yra tikrai padarytas).

Iš visų išvardytų galimybių skambinti insert visos trys yra beveik lygiavertės. Atminkite, kad pažvelkime insert parašą standarte:

 typedef pair<const Key, T> value_type;  pair<iterator, bool> insert(const value_type x); 

Taigi, kaip yra trys skirtingi skambučiai?

  • std::make_pair remiasi šablono argumento išvestimi ir gali (ir šiuo atveju) sukurti kažką kitą nei tikrasis value_type žemėlapis, kuriam reikės papildomo std::pair šablono konstruktoriaus value_type , kad galėtumėte konvertuoti į value_type (t.y. : pridėti const prie first_type
  • std::pair<int, int> taip pat reikės papildomo skambučio į std::pair šablono konstruktorių, norėdami konvertuoti parametrą į value_type ( value_type pridėti const į first_type )
  • std::map<int, int>::value_type nesuteikia jokių galimybių abejonėms, nes tai yra tikrasis parametro tipas, kurį tikisi insert nario funkcija.

Galų gale, vengsiu naudoti operator[] kai tikslą reikia įterpti, jei nėra papildomų išlaidų kuriant numatytąjį mapped_type ir priskyrus mapped_type , ir kad man nesvarbu, ar naujasis raktas bus veiksmingai įterptas. Naudojant value_type galima sukurti value_type kelią.

66
26 нояб. atsakymas duodamas icecrime 26 lapkritis 2010-11-26 19:19 '10, 19:19, 2010-11-26 19:19

Naudodami C ++ 11 turite du pagrindinius papildomus parametrus. Pirma, galite naudoti insert() su sąrašo inicijavimo sintakse:

 function.insert({0, 42}); 

Tai funkciniu požiūriu lygiavertis.

 function.insert(std::map<int, int>::value_type(0, 42)); 

bet daug glaustesnis ir aiškesnis. Kaip pažymėjo kiti atsakymai, tai turi keletą privalumų, palyginti su kitomis formomis:

  • operator[] metodui reikalingas ekrano tipo priskyrimas, o ne visada.
  • operator[] metodas gali perrašyti esamus elementus ir neleidžia žinoti, ar tai įvyko.
  • Kitos įvedimo formos, kurias nurodote, apima netiesioginį tipo konvertavimą, kuris gali sulėtinti kodą.

Pagrindinis trūkumas yra tas, kad ši forma buvo naudojama norint, kad raktas ir vertė būtų nukopijuoti, todėl jis neveiks, pavyzdžiui, žemėlapis su unique_ptr reikšmėmis. Tai buvo nustatyta standarte, tačiau pataisa gali būti dar neįgyvendinta.

Antra, galite naudoti emplace() metodą:

 function.emplace(0, 42); 

Tai labiau iškalbingas, nei bet kuri insert() forma, puikiai veikia tik su perkėlimo tipais, pvz., unique_ptr , ir teoriškai gali būti šiek tiek efektyvesnė (nors tinkamas kompiliatorius turėtų optimizuoti skirtumą). Vienintelis rimtas trūkumas yra tas, kad jis gali šiek tiek nustebinti savo skaitytojus, nes emplace metodai dažniausiai nenaudojami.

72
13 нояб. Geoff Romer atsakė į lapkričio 13 d 2013-11-13 03:21 '13, 3:21 2013-11-13 03:21

Pirmoji versija:

 function[0] = 42; // version 1 

gali įterpti į kortelę vertę 42. Jei yra 0 klavišas, tai bus priskirtas 42 šiam klavišui, perrašant bet kurią reikšmę, kurią turi raktas. Priešingu atveju įterpiama raktų / verčių pora.

Įterpti funkcijas:

 function.insert(std::map<int, int>::value_type(0, 42)); // version 2 function.insert(std::pair<int, int>(0, 42)); // version 3 function.insert(std::make_pair(0, 42)); // version 4 

kita vertus, nieko nedarykite, jei 0 klavišas jau yra žemėlapyje. Jei raktas neegzistuoja, jis įterpia raktų / verčių porą.

Trys įterpimo funkcijos yra beveik identiškos. std::map<int, int>::value_type yra std::map<int, int>::value_type std::pair<const int, int> , ir std::make_pair() akivaizdžiai gamina std::pair<> per šablono atėmimo magiją. Tačiau 2, 3 ir 4 versijose galutinis rezultatas turi būti toks pat.

Kuris naudoju? Asmeniškai norėčiau 1 versiją; Tai glaustas ir „natūralus“. Žinoma, jei jo perrašymo elgesys yra nepageidaujamas, aš norėčiau, kad būtų 4 versija, nes jai reikia mažiau teksto įvedimo nei 2 ir 3 versijos. Nežinau, ar yra de facto būdas įterpti raktų / verčių poras į std::map .

Kitas būdas įterpti vertybes į žemėlapį yra vienas iš jo konstruktorių:

 std::map<int, int> quadratic_func; quadratic_func[0] = 0; quadratic_func[1] = 1; quadratic_func[2] = 4; quadratic_func[3] = 9; std::map<int, int> my_func(quadratic_func.begin(), quadratic_func.end()); 
6
26 нояб. atsakymas pateiktas In silico 26 lapkritis. 2010-11-26 18:53 '10, 18:53, 2010-11-26 18:53

Aš palyginau laiką tarp pirmiau minėtų versijų:

 function[0] = 42; function.insert(std::map<int, int>::value_type(0, 42)); function.insert(std::pair<int, int>(0, 42)); function.insert(std::make_pair(0, 42)); 

Pasirodo, kad laikini skirtumai tarp įterpimo versijų yra nedideli.

 #include <map> #include <vector> #include <boost/date_time/posix_time/posix_time.hpp> using namespace boost::posix_time; class Widget { public: Widget() { m_vec.resize(100); for(unsigned long it = 0; it < 100;it++) { m_vec[it] = 1.0; } } Widget(double el) { m_vec.resize(100); for(unsigned long it = 0; it < 100;it++) { m_vec[it] = el; } } private: std::vector<double> m_vec; }; int main(int argc, char* argv[]) { std::map<int,Widget> map_W; ptime t1 = boost::posix_time::microsec_clock::local_time(); for(int it = 0; it < 10000;it++) { map_W.insert(std::pair<int,Widget>(it,Widget(2.0))); } ptime t2 = boost::posix_time::microsec_clock::local_time(); time_duration diff = t2 - t1; std::cout << diff.total_milliseconds() << std::endl; std::map<int,Widget> map_W_2; ptime t1_2 = boost::posix_time::microsec_clock::local_time(); for(int it = 0; it < 10000;it++) { map_W_2.insert(std::make_pair(it,Widget(2.0))); } ptime t2_2 = boost::posix_time::microsec_clock::local_time(); time_duration diff_2 = t2_2 - t1_2; std::cout << diff_2.total_milliseconds() << std::endl; std::map<int,Widget> map_W_3; ptime t1_3 = boost::posix_time::microsec_clock::local_time(); for(int it = 0; it < 10000;it++) { map_W_3[it] = Widget(2.0); } ptime t2_3 = boost::posix_time::microsec_clock::local_time(); time_duration diff_3 = t2_3 - t1_3; std::cout << diff_3.total_milliseconds() << std::endl; std::map<int,Widget> map_W_0; ptime t1_0 = boost::posix_time::microsec_clock::local_time(); for(int it = 0; it < 10000;it++) { map_W_0.insert(std::map<int,Widget>::value_type(it,Widget(2.0))); } ptime t2_0 = boost::posix_time::microsec_clock::local_time(); time_duration diff_0 = t2_0 - t1_0; std::cout << diff_0.total_milliseconds() << std::endl; system("pause"); } 

Tai suteikia atitinkamą versiją (todėl paleisčiau failą tris kartus, kiekvienam iš jų 3 kartus iš eilės):

 map_W.insert(std::pair<int,Widget>(it,Widget(2.0))); 

2198 ms, 2078 ms, 2072 ms

 map_W_2.insert(std::make_pair(it,Widget(2.0))); 

2290 ms, 2037 ms, 2046 ms

  map_W_3[it] = Widget(2.0); 

2592 ms, 2278 ms, 2296 ms

  map_W_0.insert(std::map<int,Widget>::value_type(it,Widget(2.0))); 

2234 ms, 2031 ms, 2027 ms

Todėl rezultatas tarp skirtingų įterpimo versijų gali būti ignoruojamas (nors hipotezės testas nebuvo atliktas)!

Versija map_W_3[it] = Widget(2.0); Šiam pavyzdžiui užtrunka apie 10-15% daugiau laiko, nes inicijuotas standartinis „Widget“ konstruktorius.

3
13 янв. atsakymą pateikė vartotojo3116431 sausio 13 d 2014-01-13 18:43 '14, 18:43 2014-01-13 18:43

Jei norite perrašyti elementą 0 klavišu

 function[0] = 42; 

Priešingu atveju:

 function.insert(std::make_pair(0, 42)); 
1
26 нояб. Viktoro Sehro lapkričio 26 d. Atsakymas 2010-11-26 18:53 '10, 18:53, 2010-11-26 18:53

Jei norite įterpti elementą į std :: map, naudokite įterpimo () funkciją ir, jei norite rasti elementą (pagal raktą) ir priskirti jį kažkam, naudokite operatorių [].

Jei norite supaprastinti įterpimą, naudokite padidinimo :: priskirti biblioteką, pavyzdžiui:

 using namespace boost::assign; // For inserting one element: insert( function )( 0, 41 ); // For inserting several elements: insert( function )( 0, 41 )( 0, 42 )( 0, 43 ); 
1
26 нояб. Denis Shevchenko atsakymas lapkričio 26 d. 2010-11-26 21:32 '10, 21:32, 2010-11-26 21:32

Trumpai tariant, [] operatorius yra efektyvesnis verčių atnaujinimui, nes jis apima numatytojo konstruktoriaus verifikavimą vertės tipui ir paskui priskiria jį naujai vertei, o insert() efektyvesnis verčių pridėjimui.

Čia gali padėti citata iš „Efektyvaus STL: 50 konkrečių būdų, kaip pagerinti standartinio šablono bibliotekos naudojimą“, kurią pateikė Scott Myers (24 punktas).

 template<typename MapType, typename KeyArgType, typename ValueArgType> typename MapType::iterator insertKeyAndValue(MapType m, const KeyArgType const ValueArgType v) { typename MapType::iterator lb = m.lower_bound(k); if (lb != m.end()  !(m.key_comp()(k, lb->first))) { lb->second = v; return lb; } else { typedef typename MapType::value_type MVT; return m.insert(lb, MVT(k, v)); } } 

Galite pasirinkti versiją, kuri neturi nieko bendro su programavimu, ir tai, kad manau, kad ši paradigma (diferencijuoti „pridedant“ ir „atnaujinimą“) yra labai naudinga.

1
02 марта '17 в 21:58 2017-03-02 21:58 atsakymas pateikiamas galactica kovo 02–17 d. 21:58 2017-03-02 21:58

Aš tiesiog pakeisiu problemą šiek tiek (eilutės žemėlapis), kad būtų rodomas kitas įterpimas:

 std::map<int, std::string> rancking; rancking[0] = 42; // << some compilers [gcc] show no error rancking.insert(std::pair<int, std::string>(0, 42));// always a compile error 

faktas, kad kompiliatorius neranda klaidos, kai „rancking [1] = 42;“ gali turėti niokojančio poveikio!

0
28 мая '16 в 14:13 2016-05-28 14:13 jo_ atsakymas gegužės 28 d., 16 d. 14:13 2016-05-28 14:13

Žr. Kitus klausimus apie etiketes arba Užduoti klausimą