C ++でプライベート静的constマップを初期化する方法は?


108

辞書または連想配列string=> だけが必要intです。

この場合の型マップC ++があります。

しかし、すべてのインスタンス(->静的)に必要なマップは1つだけであり、このマップは変更できません(-> const)。

私はこの方法をブーストライブラリで見つけました

 std::map<int, char> example = 
      boost::assign::map_list_of(1, 'a') (2, 'b') (3, 'c');

このlibなしの他の解決策はありますか?私はこのようなものを試しましたが、マップの初期化には常にいくつかの問題があります。

class myClass{
private:
    static map<int,int> create_map()
        {
          map<int,int> m;
          m[1] = 2;
          m[3] = 4;
          m[5] = 6;
          return m;
        }
    static map<int,int> myMap =  create_map();

};

1
あなたが言及している問題は何ですか?このマップを別のグローバル静的変数/定数から使用しようとしていますか?
ペーテルTörök

これは連想配列string => intではありません。intをcharにマッピングしています。v = k + 'a' - 1
Johnsyweb 2010年

回答:


107
#include <map>
using namespace std;

struct A{
    static map<int,int> create_map()
        {
          map<int,int> m;
          m[1] = 2;
          m[3] = 4;
          m[5] = 6;
          return m;
        }
    static const map<int,int> myMap;

};

const map<int,int> A:: myMap =  A::create_map();

int main() {
}

3
単純化のための+1、もちろんBoost.Assign同様のデザインを使用することも非常にきちんと整っています:)
Matthieu M.

5
+1、ありがとう。注:初期化行を実装ファイルに入れる必要がありました。ヘッダーファイルに残しておくと、複数の定義が原因でエラーが発生しました(ヘッダーがどこかに含まれている場合は常に初期化コードが実行されます)。
System.Cats.Lol

1
g ++ v4.7.3では、に追加cout << A::myMap[1];するまで、これはコンパイルされmain()ます。エラーが発生します。const修飾子を削除してもエラーは発生しないため、少なくともC ++ライブラリのg ++​​実装では、マップoperator[]がを処理できないと思いますconst map
Craig McQueen

2
エラーがある:const_map.cpp:22:23: error: passing ‘const std::map<int, int>’ as ‘this’ argument of ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = int; _Tp = int; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, int> >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = int; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]’ discards qualifiers [-fpermissive]
クレイグ・マックイーン

4
確かに、マップのoperator []はconstマップを操作できません。これは、そのマップされた値への参照を返すため、そのオペレーターは参照されたエントリが存在しない場合にそれを作成するためです。C ++ 11には、指定されたキーで項目にアクセスできるat(KeyValT key)メソッドが導入され、存在しない場合は例外がスローされます。(en.cppreference.com/w/cpp/container/map/at)このメソッドはconstインスタンスで機能しますが、([]演算子のように)非constインスタンスに要素を挿入するために使用することはできません。
mbargiel 2014

108

C ++ 11標準では、コンパイラーがサポートしている場合に、これをより簡単にする統一された初期化が導入されました。

//myClass.hpp
class myClass {
  private:
    static map<int,int> myMap;
};


//myClass.cpp
map<int,int> myClass::myMap = {
   {1, 2},
   {3, 4},
   {5, 6}
};

unordered_mapsについては、Professional C ++のこのセクションも参照してください。


cppファイルに等号が必要ですか?
phoad 2017

@phoad:等号は不要です。
2017年

使い方をご提示いただきありがとうございます。静的変数を変更する方法を理解することは本当に役に立ちました。
User9102d82

12

やったよ!:)

C ++ 11がなくても正常に動作します

class MyClass {
    typedef std::map<std::string, int> MyMap;

    struct T {
        const char* Name;
        int Num;

        operator MyMap::value_type() const {
            return std::pair<std::string, int>(Name, Num);
        }
    };

    static const T MapPairs[];
    static const MyMap TheMap;
};

const MyClass::T MyClass::MapPairs[] = {
    { "Jan", 1 }, { "Feb", 2 }, { "Mar", 3 }
};

const MyClass::MyMap MyClass::TheMap(MapPairs, MapPairs + 3);

11

あなたがboost::assign::map_list_of便利だと思っても、何らかの理由でそれを使用できない場合は、独自に書くことができます

template<class K, class V>
struct map_list_of_type {
  typedef std::map<K, V> Map;
  Map data;
  map_list_of_type(K k, V v) { data[k] = v; }
  map_list_of_type& operator()(K k, V v) { data[k] = v; return *this; }
  operator Map const&() const { return data; }
};
template<class K, class V>
map_list_of_type<K, V> my_map_list_of(K k, V v) {
  return map_list_of_type<K, V>(k, v);
}

int main() {
  std::map<int, char> example = 
    my_map_list_of(1, 'a') (2, 'b') (3, 'c');
  cout << example << '\n';
}

そのようなものがどのように機能するかを知ることは、特にそれらが非常に短い場合に役立ちますが、この場合は関数を使用します。

a.hpp

struct A {
  static map<int, int> const m;
};

a.cpp

namespace {
map<int,int> create_map() {
  map<int, int> m;
  m[1] = 2; // etc.
  return m;
}
}

map<int, int> const A::m = create_map();

6

問題に対する別のアプローチ:

struct A {
    static const map<int, string> * singleton_map() {
        static map<int, string>* m = NULL;
        if (!m) {
            m = new map<int, string>;
            m[42] = "42"
            // ... other initializations
        }
        return m;
    }

    // rest of the class
}

スタックからヒープへの1種類のコピー(すべての要素のコンストラクタ、デストラクタを含む)がないため、これはより効率的です。これが問題になるかどうかは、ユースケースによって異なります。文字列は関係ありません!(ただし、このバージョンが「よりクリーン」であるかどうかはわかりません)


3
RVOは私のコピーとNeilの答えを排除します。

6

マップにコンパイル時に既知のエントリのみが含まれ、マップへのキーが整数である場合、マップを使用する必要はありません。

char get_value(int key)
{
    switch (key)
    {
        case 1:
            return 'a';
        case 2:
            return 'b';
        case 3:
            return 'c';
        default:
            // Do whatever is appropriate when the key is not valid
    }
}

5
地図が必要ないことを指摘するための+1、ただしこれを繰り返すことはできません
Viktor Sehr

4
しかし、それswitchはひどいです。なんでreturn key + 'a' - 1
Johnsyweb 2010年

12
@Johnsyweb。元のポスターによって提供されたマッピングは単なる例として提示されたものであり、彼が持っている実際のマッピングを示すものではないと思います。したがって、return key + 'a' - 1彼の実際のマッピングでは機能しないことも想定しています。
マシューT.ステイブラー

3

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

MyClass.h

class MyClass {
private:
    static const std::map<key, value> m_myMap; 
    static const std::map<key, value> createMyStaticConstantMap();
public:
    static std::map<key, value> getMyConstantStaticMap( return m_myMap );
}; //MyClass

MyClass.cpp

#include "MyClass.h"

const std::map<key, value> MyClass::m_myMap = MyClass::createMyStaticConstantMap();

const std::map<key, value> MyClass::createMyStaticConstantMap() {
    std::map<key, value> mMap;
    mMap.insert( std::make_pair( key1, value1 ) );
    mMap.insert( std::make_pair( key2, value2 ) );
    // ....
    mMap.insert( std::make_pair( lastKey, lastValue ) ); 
    return mMap;
} // createMyStaticConstantMap

この実装では、クラス定数静的マップはプライベートメンバーであり、パブリックのgetメソッドを使用して他のクラスにアクセスできます。それ以外の場合は定数であり変更できないため、public getメソッドを削除して、マップ変数をクラスのpublicセクションに移動できます。ただし、継承やポリモーフィズムが必要な場合は、createMapメソッドをプライベートまたは保護したままにします。以下に使用例をいくつか示します。

 std::map<key,value> m1 = MyClass::getMyMap();
 // then do work on m1 or
 unsigned index = some predetermined value
 MyClass::getMyMap().at( index ); // As long as index is valid this will 
 // retun map.second or map->second value so if in this case key is an
 // unsigned and value is a std::string then you could do
 std::cout << std::string( MyClass::getMyMap().at( some index that exists in map ) ); 
// and it will print out to the console the string locted in the map at this index. 
//You can do this before any class object is instantiated or declared. 

 //If you are using a pointer to your class such as:
 std::shared_ptr<MyClass> || std::unique_ptr<MyClass>
 // Then it would look like this:
 pMyClass->getMyMap().at( index ); // And Will do the same as above
 // Even if you have not yet called the std pointer's reset method on
 // this class object. 

 // This will only work on static methods only, and all data in static methods must be available first.

私は自分の元の投稿を編集しましたが、コンパイルして構築し、正しく実行したために投稿した元のコードに問題はありませんでした。回答として提示した最初のバージョンは、マップがパブリックとして宣言され、マップがconstは静的ではありませんでした。


2

ユニバーサル初期化をまだサポートしていないコンパイラーを使用している場合、またはBoostの使用を予約している場合、別の可能な方法は次のとおりです。

std::map<int, int> m = [] () {
    std::pair<int,int> _m[] = {
        std::make_pair(1 , sizeof(2)),
        std::make_pair(3 , sizeof(4)),
        std::make_pair(5 , sizeof(6))};
    std::map<int, int> m;
    for (auto data: _m)
    {
        m[data.first] = data.second;
    }
    return m;
}();

0

関数呼び出しは定数式では使用できません。

これを試してください:(単なる例)

#include <map>
#include <iostream>

using std::map;
using std::cout;

class myClass{
 public:
 static map<int,int> create_map()
    {
      map<int,int> m;
      m[1] = 2;
      m[3] = 4;
      m[5] = 6;
      return m;
    }
 const static map<int,int> myMap;

};
const map<int,int>myClass::myMap =  create_map();

int main(){

   map<int,int> t=myClass::create_map();
   std::cout<<t[1]; //prints 2
}

6
関数は確かにconstオブジェクトを初期化するために使用できます。

OPのコードstatic map<int,int> myMap = create_map();が正しくありません。
Prasoon Saurav 2010

3
問題のコードは間違っています、私たちは皆それに同意しますが、この回答で言うように「定数式」とは関係ありませんが、むしろクラスの定数静的メンバーしか初期化できないという事実整数型または列挙型の場合の宣言。他のすべてのタイプの場合、初期化は宣言ではなくメンバー定義で行う必要があります。
デビッドロドリゲス-dribeas

Neilの回答はg ++でコンパイルされます。それでも、GNUツールチェーンの以前のバージョンでこのアプローチに問題があったことを覚えています。普遍的な正しい答えはありますか?
バシレフ2010

1
@Prasoon:コンパイラの言うことはわかりませんが、質問コードのエラーは、初期化が定数式であるかどうかに関係なく、クラス宣言でクラス型の定数メンバー属性を初期化しています。クラスを定義した場合:struct testdata { testdata(int){} }; struct test { static const testdata td = 5; }; testdata test::td;初期化が定数式(5)で実行されても、コンパイルは失敗します。つまり、「定数式」は、初期コードの正確性(またはその欠如)とは無関係です。
DavidRodríguez-dribeas

-2

私はこのパターンを頻繁に使用し、それも使用することをお勧めします:

class MyMap : public std::map<int, int>
{
public:
    MyMap()
    {
        //either
        insert(make_pair(1, 2));
        insert(make_pair(3, 4));
        insert(make_pair(5, 6));
        //or
        (*this)[1] = 2;
        (*this)[3] = 4;
        (*this)[5] = 6;
    }
} const static my_map;

確かにそれはあまり読みやすいものではありませんが、他のライブラリがなければ、私たちができる最善の方法です。また、あなたの試みのように、あるマップから別のマップにコピーするような冗長な操作はありません。

これは関数の中でさらに便利です:代わりに:

void foo()
{
   static bool initComplete = false;
   static Map map;
   if (!initComplete)
   {
      initComplete = true;
      map= ...;
   }
}

以下を使用します。

void bar()
{
    struct MyMap : Map
    {
      MyMap()
      {
         ...
      }
    } static mymap;
}

ここでブール変数を処理する必要がないだけでなく、関数内の静的変数の初期化子がすでに呼び出されているかどうかをチェックする非表示のグローバル変数もありません。


6
継承は、最初の手段ではなく、最後の手段のツールであるべきです。

RVOをサポートするコンパイラーは、関数バージョンでの重複コピーを排除します。C ++ 0x移動セマンティクスは、利用可能になると残りを排除します。いずれにせよ、それがボトルネックに近いとは思えません。

ロジャー、私はRVOと&&とムーブのセマンティクスをよく知っています。これは、今のところ最小限のコードとエンティティでのソリューションです。さらに、関数の内部で関数を定義することが許可されていないため、すべてのC ++ 0x機能は、関数の例の内部の静的オブジェクトには役立ちません。
Pavel Chikulaev 2010
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.