Initialiseren van een STATIC STD :: MAP & LT; INT, INT & GT; In C++

Wat is de juiste manier om een ​​statische kaart te initialiseren? Hebben we een statische functie nodig die het initialiseert?


Antwoord 1, Autoriteit 100%

Gebruik C++ 11:

#include <map>
using namespace std;
map<int, char> m = {{1, 'a'}, {3, 'b'}, {5, 'c'}, {7, 'd'}};

Boost.Assign :

#include <map>
#include "boost/assign.hpp"
using namespace std;
using namespace boost::assign;
map<int, char> m = map_list_of (1, 'a') (3, 'b') (5, 'c') (7, 'd');

Antwoord 2, Autoriteit 21%

De beste manier is om een ​​functie te gebruiken:

#include <map>
using namespace std;
map<int,int> create_map()
{
  map<int,int> m;
  m[1] = 2;
  m[3] = 4;
  m[5] = 6;
  return m;
}
map<int,int> m = create_map();

Antwoord 3, Autoriteit 17%

Het is geen gecompliceerde kwestie om iets vergelijkbaars te maken met boost. Hier is een klasse met slechts drie functies, inclusief de constructor, om te repliceren welke boost (bijna).

template <typename T, typename U>
class create_map
{
private:
    std::map<T, U> m_map;
public:
    create_map(const T& key, const U& val)
    {
        m_map[key] = val;
    }
    create_map<T, U>& operator()(const T& key, const U& val)
    {
        m_map[key] = val;
        return *this;
    }
    operator std::map<T, U>()
    {
        return m_map;
    }
};

Gebruik:

std::map mymap = create_map<int, int >(1,2)(3,4)(5,6);

De bovenstaande code werkt het beste voor de initialisatie van globale variabelen of statische leden van een klasse die moet worden geïnitialiseerd en je hebt geen idee wanneer deze voor het eerst wordt gebruikt, maar je wilt er zeker van zijn dat de waarden erin beschikbaar zijn.

>

Als je bijvoorbeeld elementen moet invoegen in een bestaande std::map… dan is hier een andere klasse voor jou.

template <typename MapType>
class map_add_values {
private:
    MapType mMap;
public:
    typedef typename MapType::key_type KeyType;
    typedef typename MapType::mapped_type MappedType;
    map_add_values(const KeyType& key, const MappedType& val)
    {
        mMap[key] = val;
    }
    map_add_values& operator()(const KeyType& key, const MappedType& val) {
        mMap[key] = val;
        return *this;
    }
    void to (MapType& map) {
        map.insert(mMap.begin(), mMap.end());
    }
};

Gebruik:

typedef std::map<int, int> Int2IntMap;
Int2IntMap testMap;
map_add_values<Int2IntMap>(1,2)(3,4)(5,6).to(testMap);

Bekijk het in actie met GCC 4.7.2 hier: http://ideone.com/3uYJiH

############### ALLES HIERONDER IS VEROUDERD #################

EDIT: de klasse map_add_valueshieronder, de oorspronkelijke oplossing die ik had voorgesteld, zou falen als het gaat om GCC 4.5+. Bekijk de bovenstaande code voor het toevoegenvan waarden aan een bestaande kaart.


template<typename T, typename U>
class map_add_values
{
private:
    std::map<T,U>& m_map;
public:
    map_add_values(std::map<T, U>& _map):m_map(_map){}
    map_add_values& operator()(const T& _key, const U& _val)
    {
        m_map[key] = val;
        return *this;
    }
};

Gebruik:

STD :: MAP & LT; INT, INT & GT; my_map;
// later ergens in de code
MAP_ADD_VALUES & LT; INT, INT & GT; (MY_MAP) (1,2) (3,4) (5,6); 

Opmerking: eerder heb ik een operator []gebruikt voor het toevoegen van de werkelijke waarden. Dit is niet mogelijk zoals gereageerd door Dalle.

###################### Einde van verouderde gedeelte ################################################ ##


Antwoord 4, Autoriteit 7%

Hier is een andere manier die de gegevensconstructeur van 2-element gebruikt. Er zijn geen functies nodig om het initialiseren. Er is geen 3e partijcode (boost), geen statische functies of objecten, geen trucs, gewoon eenvoudig C++:

#include <map>
#include <string>
typedef std::map<std::string, int> MyMap;
const MyMap::value_type rawData[] = {
   MyMap::value_type("hello", 42),
   MyMap::value_type("world", 88),
};
const int numElems = sizeof rawData / sizeof rawData[0];
MyMap myMap(rawData, rawData + numElems);

Sinds ik dit antwoord schreef, is C++ 11 uit. U kunt nu rechtstreeks STL-containers initialiseren met behulp van de functie Nieuwe Initializer:

const MyMap myMap = { {"hello", 42}, {"world", 88} };

Antwoord 5, Autoriteit 4%

Bijvoorbeeld:

const std::map<LogLevel, const char*> g_log_levels_dsc =
{
    { LogLevel::Disabled, "[---]" },
    { LogLevel::Info,     "[inf]" },
    { LogLevel::Warning,  "[wrn]" },
    { LogLevel::Error,    "[err]" },
    { LogLevel::Debug,    "[dbg]" }
};

Als map een datalid is van een klasse, kun je deze op de volgende manier direct in de header initialiseren (sinds C++17):

// Example
template<>
class StringConverter<CacheMode> final
{
public:
    static auto convert(CacheMode mode) -> const std::string&
    {
        // validate...
        return s_modes.at(mode);
    }
private:
    static inline const std::map<CacheMode, std::string> s_modes =
        {
            { CacheMode::All, "All" },
            { CacheMode::Selective, "Selective" },
            { CacheMode::None, "None" }
            // etc
        };
}; 

Antwoord 6, autoriteit 4%

Ik zou de kaart in een statisch object wikkelen en de kaartinitialisatiecode in de constructor van dit object plaatsen, op deze manier weet je zeker dat de kaart is gemaakt voordat de initialisatiecode wordt uitgevoerd.


Antwoord 7, autoriteit 3%

Ik wilde gewoon een pure C++ 98 work-around delen:

#include <map>
std::map<std::string, std::string> aka;
struct akaInit
{
    akaInit()
    {
        aka[ "George" ] = "John";
        aka[ "Joe" ] = "Al";
        aka[ "Phil" ] = "Sue";
        aka[ "Smitty" ] = "Yando";
    }
} AkaInit;

Antwoord 8, autoriteit 2%

Je kunt het proberen:

std::map <int, int> mymap = 
{
        std::pair <int, int> (1, 1),
        std::pair <int, int> (2, 2),
        std::pair <int, int> (2, 2)
};

Antwoord 9

Dit is vergelijkbaar met PierreBdR, zonder de kaart te kopiëren.

#include <map>
using namespace std;
bool create_map(map<int,int> &m)
{
  m[1] = 2;
  m[3] = 4;
  m[5] = 6;
  return true;
}
static map<int,int> m;
static bool _dummy = create_map (m);

Antwoord 10

Als u vastzit met C++ 98 en geen boost wilt gebruiken, is hier de oplossing die ik gebruik wanneer ik een statische kaart moet initialiseren:

typedef std::pair< int, char > elemPair_t;
elemPair_t elemPairs[] = 
{
    elemPair_t( 1, 'a'), 
    elemPair_t( 3, 'b' ), 
    elemPair_t( 5, 'c' ), 
    elemPair_t( 7, 'd' )
};
const std::map< int, char > myMap( &elemPairs[ 0 ], &elemPairs[ sizeof( elemPairs ) / sizeof( elemPairs[ 0 ] ) ] );

Antwoord 11

Naast het goede antwoord van het gebruik van

const std::map<int, int> m = {{1,1},{4,2},{9,3},{16,4},{32,9}}

Er is een extra mogelijkheid door een lambda rechtstreeks te bellen die nuttig kan zijn in een paar gevallen:

const std::map<int, int> m = []()->auto {
  std::map<int, int> m;
  m[1]=1;
  m[4]=2;
  m[9]=3;
  m[16]=4;
  m[32]=9;
  return m;
}();

Het is duidelijk dat een eenvoudige initializerlijst beter is bij het schrijven van dit helemaal opnieuw met letterlijke waarden, maar het opent extra mogelijkheden:

const std::map<int, int> m = []()->auto {
  std::map<int, int> m;
  for(int i=1;i<5;++i) m[i*i]=i;
  m[32]=9;
  return m;
}();

(Natuurlijk zou het een normale functie moeten zijn als je het opnieuw wilt gebruiken; en dit vereist wel recente C++.)


Antwoord 12

Je hebt hier een aantal zeer goede antwoorden, maar ik ben voor mij, het lijkt op een geval van “wanneer alles wat je weet een hamer is”…

Het eenvoudigste antwoord op de vraag waarom er geen standaardmanier is om een statische kaart te initialiseren, is dat er geen goede reden is om ooit een statische kaart te gebruiken…

Een kaart is een structuur die is ontworpen om snel een onbekende set elementen op te zoeken. Als je de elementen van tevoren kent, gebruik dan gewoon een C-array. Voer de waarden gesorteerd in, of voer sorteer erop uit, als u dit niet kunt doen. U kunt dan log(n)-prestaties verkrijgen door de stl::functions te gebruiken om items, lower_bound/upper_bound, door te lussen. Als ik dit eerder heb getest, presteren ze normaal gesproken minstens 4 keer sneller dan een kaart.

De voordelen zijn veelvoudig…
– snellere prestaties (*4, ik heb op veel CPU-types gemeten, het is altijd rond de 4)
– eenvoudiger debuggen. Het is gewoon gemakkelijker om te zien wat er aan de hand is met een lineaire lay-out.
– Triviale implementaties van kopieerbewerkingen, mocht dat nodig zijn.
– Het wijst tijdens runtime geen geheugen toe, dus er wordt nooit een uitzondering gegenereerd.
– Het is een standaardinterface en is dus heel gemakkelijk te delen via DLL’s of talen, enz.

Ik zou kunnen doorgaan, maar als je meer wilt, kijk dan eens naar de vele blogs van Stroustrup over dit onderwerp.

Other episodes