在C ++中初始化静态std :: map <int,int>


447

初始化静态地图的正确方法是什么?我们需要一个可以初始化它的静态函数吗?

Answers:


618

使用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 ++完成的事情时,我都会想到它背后必须有所有可怕的模板代码。好的例子!
格雷格(Greg Hewgill)

34
实现这些实用程序的所有可怕的模板代码的优点在于,它们被整齐地封装在一个库中,并且最终用户很少需要处理复杂性。
史蒂夫·吉迪

45
@QBziZ:如果您的公司基于“标准不够”而拒绝使用Boost,我想知道哪种C ++库 “标准”。Boost是C ++编码器标准伴侣。
DevSolar 2012年

47
Boost的问题(在这里和其他地方)是我经常遇到的问题(在这种情况下使用C ++ 11或在C ++ 11之前使用function)。Boost增加了可观的编译时间开销,将大量文件存储在存储库中(如果要创建存档,则必须四处复制/压缩/提取)。这就是我尝试不使用它的原因。我知道您可以选择要包含/不包含的文件,但是通常您不必担心Boost自身的交叉依赖关系,因此您只需复制整个内容即可。
bobobobo

7
Boost的问题是它通常具有几个新的库依赖关系,这通常意味着需要安装更多软件包才能正常工作。我们已经需要libstdc ++。例如,Boost ASIO库需要至少安装2个(可能更多)新库。C ++ 11/14确实使不需要Boost变得容易得多。
拉利

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的答案更好?
洛恩侯爵(Marquis of Lorne)

6
我认为它是“最佳”的,因为它非常简单并且不依赖于现有的其他结构(例如Boost :: Assign或对其的重新实现)。与@Dreamer的答案相比,好吧,我避免创建仅用于初始化地图的整体结构...
PierreBdR

3
注意这里有危险如果编译器仅看到该声明,但尚未运行到实际的变量定义中,则该extern变量将在“运行时主构造函数之前”中没有正确的值。extern
bobobobo

5
不,这样做的危险是没有说静态变量应该以哪个顺序初始化(至少在编译单元中)。但这不是与此问题相关的问题。这是静态变量的普遍问题。
PierreBdR

5
没有提升,也没有C ++ 11 => +1。请注意,该函数可用于初始化a const map<int,int> m = create_map()(因此,可在初始化列表中初始化类的const成员:struct MyClass {const map<int, int> m; MyClass(); }; MyClass::MyClass() : m(create_map())
ribamar

115

进行类似于boost的事情并不复杂。这是一个只有三个函数(包括构造函数)的类,用于(几乎)复制boost所做的工作。

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

1
operator[]只需要一个参数。
dalle

1
@dalle:不错!由于某些原因,我认为重载[]运算符可以接受更多内容。
Vite Falcon,

2
这是一个了不起的答案。OP从未选择过,这很可惜。您值得拥有大型道具。
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

2
塔德比什么还快?带有初始化程序的全局静态变量?不,不是(记住RVO)。
帕维尔·米纳夫

7
好答案。如果我看到实际的示例代码,我将很高兴
Sungguk Lim 2014年

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
这对于没有默认构造函数的对象不起作用,应该首选插入方法恕我直言
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

2
但是这种方式map不能是静态const,不是吗?
xmoex

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

5
如果可以使用数组,则数组要容易得多,并且性能更高。但是,如果索引(键)不是连续的,并且间距很大,则需要一张地图。
KarlU 2014年

1
A map也是表示部分函数(数学意义上的函数;但从编程意义上来说也是一种)的有用形式。数组不这样做。例如,您不能使用字符串从数组中查找数据。
einpoklum 2015年

3
您的答案不会尝试回答有效的问题,而是推测语言的局限性,针对不同的问题提出解决方案,因此投反对票。一个真实的场景-将库错误代码映射(连续或不连续)到文本字符串。使用数组时,搜索时间为O(n),可以通过静态映射到O(log(n))来改善。
Tosha

2
如果确实“没有充分理由使用静态映射...”,那么在C ++ 11中添加了使它们易于使用的语法(初始化程序列表),这是很奇怪的。
ellisbben
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.