C ++での静的std :: map <int、int>の初期化


447

静的マップを初期化する正しい方法は何ですか?それを初期化する静的関数が必要ですか?

回答:


619

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');

115
C ++でそのようなことが行われるのを目にするたびに、背後にあるすべての恐ろしいテンプレートコードについて考えます。良い手本!
グレッグヒューギル

34
これらのユーティリティを実装するすべての恐ろしいテンプレートコードの美しさは、ライブラリにきちんとカプセル化されており、エンドユーザーがめったに複雑さに対処する必要がないことです。
スティーブギディ

45
@QBziZ:「十分に標準的」ではないという理由であなたの会社がBoostの使用を拒否した場合、どのC ++ライブラリが「十分に標準的」であるのでしょうか。Boostは C ++コーダー標準的な付属品です。
DevSolar 2012年

47
Boostに関する私の問題(ここ、および他の場所)は、それなしで(この場合はC ++ 11を使用するか、C ++ 11 を使用する前に)関数を実行できることが多いということです。Boostを使用すると、コンパイル時間のオーバーヘッドが大幅に増加し、リポジトリに大量のファイルを駐車する必要がありました(アーカイブを作成する場合は、周りをコピー/ zip / extractする必要があります)。それが私がそれを使わないようにする理由です。インクルードするファイルとインクルードしないファイルを選択できることはわかっていますが、通常はBoostの相互依存関係を気にする必要がないので、全体をコピーします。
bobobobo 2013年

7
Boostに関する私の問題は、多くの場合、いくつかの新しいライブラリの依存関係があることです。これは、通常、正しく動作するためにインストールする必要があるより多くのパッケージを意味します。すでにlibstdc ++が必要です。たとえば、Boost ASIOライブラリには、少なくとも2つの新しいライブラリ(おそらくそれ以上)をインストールする必要があります。C ++ 11/14は、Boostを必要としないことをずっと簡単にします。
ラーリー2016年

135

最良の方法は関数を使用することです:

#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();

18
なぜこれが「最高」なのですか?たとえば、@ Dreamerの回答よりも優れているのはなぜですか?
ローンの侯爵2013年

6
本当にシンプルであり、他の既存の構造(Boost :: Assignやその再実装など)に依存しないため、「最高」だと思います。
@Dreamer

3
ここには危険があることに注意してくださいコンパイラーが宣言を見ただけで、実際の変数定義にまだ到達していない場合、externこの「メインランタイムコンストラクターの前」では、変数は正しい値を持ちませんextern
bobobobo 2013年

5
いいえ、危険なのは、静的変数を(少なくともコンパイルユニット全体で)初期化する順序が何も言われていないことです。しかし、これはこの質問に関連する問題ではありません。これは、静的変数の一般的な問題です。
PierreBdR 2013年

5
ブーストなし、C ++ 11なし=> +1。お知らせ機能を初期化するために使用できることconst map<int,int> m = create_map():(そのため、初期化リスト内のクラスのconstのメンバーを初期化するstruct MyClass {const map<int, int> m; MyClass(); }; MyClass::MyClass() : m(create_map())
ribamar

115

ブーストのようなものを作ることは複雑な問題ではありません。これは、ブーストが(ほぼ)実行したことを再現するための、コンストラクタを含む3つの関数のみを含むクラスです。

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;
    }
};

使用法:

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

上記のコードは、初期化する必要のあるクラスのグローバル変数または静的メンバーの初期化に最適であり、いつ最初に使用されるかわからないが、値が確実に使用できるようにする必要があります。

もし言うなら、要素を既存のstd :: mapに挿入しなければなりません...これがあなたのための別のクラスです。

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());
    }
};

使用法:

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

こちらのGCC 4.7.2での動作をご覧ください:http : //ideone.com/3uYJiH

###############以下はすべて廃止#################

編集map_add_values以下のクラスは、私が提案した元のソリューションでしたが、GCC 4.5以降になると失敗します。既存のマップに値を追加する方法については、上記のコードをご覧ください。


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;
    }
};

使用法:

std :: map <int、int> my_map;
//後でコードのどこかに
map_add_values <int、int>(my_map)(1,2)(3,4)(5,6);

注:以前はoperator []、実際の値を追加するためにを使用しました。dalleのコメントによると、これは不可能です。

#####################廃止セクションの終わり#####################


3
私は最初のサンプルを<int、string>として使用して(列挙型からの)エラー番号をメッセージにバインドしています-それは魅力のように機能しています-ありがとうございます。
slashmais 10/09/22

1
operator[]引数は1つだけです。
dalle

1
@dalle:グッドキャッチ!なんらかの理由で、オーバーロードされた[]演算子はそれ以上を受け入れることができると思いました。
Vite Falcon、

2
これは素晴らしい答えです。OPが1つも選択しなかったのは残念です。あなたはメガプロップに値します。
Thomas Thorogood 2012

map_add_valuesはgccでは機能せず、文句を言います: error: conflicting declaration ‘map_add_values<int, int> my_map’ error: ‘my_map’ has a previous declaration as ‘std::map<int, int> my_map’
Martin Wang

42

2要素のデータコンストラクターを使用する別の方法を次に示します。それを初期化するための関数は必要ありません。サードパーティのコード(Boost)、静的な関数やオブジェクト、トリックはなく、単純な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);

私がこの回答を書いたので、C ++ 11は出ました。新しいイニシャライザリスト機能を使用して、STLコンテナを直接初期化できるようになりました。

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

25

例えば:

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

mapがクラスのデータメンバーである場合、次の方法でヘッダーで直接初期化できます(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
        };
}; 

24

静的オブジェクト内にマップをラップし、このオブジェクトのコンストラクターにマップ初期化コードを配置します。これにより、初期化コードが実行される前にマップが作成されることが確実になります。


1
私はこれであなたと一緒です。また、少し速くなります:)
QBziZ '09 / 09/26

2
何より少し早く?イニシャライザを持つグローバルスタティック?いいえ、そうではありません(RVOを思い出してください)。
Pavel Minaev 2009年

7
いい答えだ。私は実際のコード例見れば、私は幸せになる
Sunggukリム

18

純粋なC ++ 98の回避策を共有したかっただけです。

#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;

2
これは、デフォルトのコンストラクターがないオブジェクトでは機能しません。挿入メソッドを優先する必要がありますIMHO
Alessandro Teruzzi

16

あなたが試すことができます:

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

1
C ++ 11より前の非集約型の初期化子リストは使用できません。その場合、の{1, 2}代わりに短い構文を使用することもできますstd::pair<int, int>(1, 2)
Ferruccio

9

これはに似てPierreBdRいますが、マップをコピーしません。

#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);

12
それはおそらくとにかくコピーされなかったでしょう。
GManNickG 2009年

2
しかし、この方法では、マップを静的な定数にすることはできませんでしたか?
xmoex 2013年

6

C ++ 98で立ち往生していて、boostを使用したくない場合は、静的マップを初期化する必要があるときに使用する解決策を次に示します。

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 ] ) ] );

-4

あなたはここでいくつかの非常に良い答えを持っていますが、私には、「あなたが知っているすべてがハンマーであるとき」の場合のように見えます...

静的マップを初期化する標準的な方法がない理由の最も簡単な答えは、静的マップを使用する正当な理由がないということです...

マップは、未知の要素のセットを高速に検索するために設計された構造です。事前に要素がわかっている場合は、C配列を使用してください。ソートされた方法で値を入力するか、これができない場合は、ソートを実行します。次に、stl :: functionsを使用してエントリ、lower_bound / upper_boundをループアップすることにより、log(n)のパフォーマンスを取得できます。私が以前にこれをテストしたとき、それらは通常、マップより少なくとも4倍速く実行されます。

利点はたくさんあります...-より速いパフォーマンス(* 4、私は多くのCPUのタイプで測定しました、それは常に約4です)-より簡単なデバッグ。線形レイアウトで何が起こっているかを確認するのは簡単です。-コピー操作の簡単な実装が必要になった場合。-実行時にメモリを割り当てないため、例外がスローされることはありません。-これは標準インターフェースなので、DLLや言語などで簡単に共有できます。

続けることもできますが、もっと知りたい場合は、この件に関するStroustrupの多くのブログをご覧ください。


8
マップを使用する理由はパフォーマンスだけではありません。たとえば、値をリンクしたい場合(たとえば、エラーメッセージを含むエラーコード)が多く、マップを使用するとアクセスとアクセスが比較的簡単になります。しかし、これらのブログエントリへのリンクは興味深いかもしれません。おそらく私は何か間違ったことをしています。
MatthiasB 14

5
配列を使用すると、配列がはるかに簡単になり、パフォーマンスが向上します。ただし、インデックス(キー)が隣接しておらず、間隔が広い場合は、マップが必要です。
KarlU 2014年

1
A mapは、部分的な関数(数学的な意味での関数ですが、プログラミングの意味でもの関数)を表すのにも役立ちます。配列はそれを行いません。たとえば、文字列を使用して配列からデータを検索することはできません。
einpoklum 2015

3
あなたの答えは有効な質問に答えようとするのではなく、代わりに言語の制限について推測し、さまざまな問題の解決策を提案するため、反対票を投じます。実際のシナリオ-(連続的であるかどうかに関係なく)ライブラリエラーコードをテキスト文字列にマッピングします。配列では、検索時間はO(n)ですが、O(log(n))への静的マッピングによって改善できます。
Tosha

2
実際、「静的マップを使用する正当な理由がない...」という場合、C ++ 11でそれらを使いやすくする構文(イニシャライザリスト)が追加されたことは非常に奇妙です。
ellisbben
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.