如何在C ++中初始化私有静态const映射?


108

我只需要字典或关联数组string=>即可int

这种情况下有类型映射C ++。

但是对于所有实例,我只需要一个映射(->静态),并且该映射不能更改(-> const);

我已经在Boost库中找到了这种方式

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

没有这个库,还有其他解决方案吗?我已经尝试过类似的方法,但是地图初始化总是存在一些问题。

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

这不是关联数组字符串=> int,您正在将int映射到char。v = k + 'a' - 1
Johnsyweb

Answers:


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 2012年

1
在g ++ v4.7.3中,它将进行编译,直到添加cout << A::myMap[1];到为止main()。它给出了一个错误。如果删除const限定符,则不会发生该错误,因此我猜map 至少在C ++库的g ++实现中operator[]不能处理a const map
2013年

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]
Craig McQueen

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

另请参阅来自Professional C ++的本节,关于unordered_maps。


我们在cpp文件中是否需要等号?
pho17'Apr 21'at

@phoad:等号是多余的。
进阶

感谢您显示用法。了解如何修改静态变量确实很有帮助。
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';
}

了解这些事情的工作方式很有用,特别是当它们是如此之短时,但是在这种情况下,我将使用一个函数:

hpp

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

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
}

这是更有效的,因为从堆栈到堆之间没有一种类型的副本(包括所有元素上的构造函数,析构函数)。这是否重要取决于您的用例。字符串无关紧要!(但是您可能会或可能不会发现此版本“清洁器”)


3
RVO消除了我的复制和尼尔的答案。

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 2010年

4
不过,那switch太可怕了。为什么不return key + 'a' - 1呢?
Johnsyweb

12
@Johnsyweb。我假设原始海报提供的映射仅作为示例提供,并不表示其实际映射。因此,我还要假设这return key + 'a' - 1不适用于他的实际映射。
Matthew T. Staebler 2010年

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方法并将map变量移到class 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'4

3
问题中的代码是错误的,我们都同意,但这与您在此答案中所说的“常量表达式”无关,而是因为您只能在声明它们是整数还是枚举类型。对于所有其他类型,初始化必须在成员定义而非声明中完成。
大卫·罗德里格斯(DavidRodríguez)-dribeas 2010年

Neil的答案使用g ++进行编译。仍然,我记得在早期版本的GNU工具链中这种方法存在一些问题。有普遍的正确答案吗?
Basilevs 2010年

1
@Prasoon:我不知道编译器在说什么,但是问题代码中的错误是在类声明中初始化了类类型的常量成员属性,而不管初始化是否为常量表达式。如果定义一个类:struct testdata { testdata(int){} }; struct test { static const testdata td = 5; }; testdata test::td;即使使用常量表达式(5)进行初始化,它也将无法编译。也就是说,“常量表达式”与初始代码的正确性(或缺乏正确性)无关。
大卫·罗德里格斯(DavidRodríguez)-dribeas 2010年

-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 move语义就消除了其余的语义。无论如何,我怀疑这将近成为瓶颈。

罗杰,我很了解RVO,&&并移动了语义。目前,这是使用最少的代码和实体的解决方案。另外,所有C ++ 0x功能都无法帮助函数示例中的静态对象,因为我们不允许在函数内部定义函数。
Pavel Chikulaev'4
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.