【问题标题】:How to create a std::map of constant values which is still accessible by the [] operator?如何创建仍可由 [] 运算符访问的常量值的 std::map?
【发布时间】:2013-05-23 08:01:47
【问题描述】:

我需要一个只读的 std:map 数据结构,这意味着我必须用数据填充一次,然后只读取这些值,永远不要更改它们或添加其他值。

我的非常量版本如下所示:

//in .h
#include <string>
#include <map>

std::map<std::string, int> myMap;
void initMap();

//in .cpp
#include "foo.h"

void initMap() {
  myMap["Keys"] = 42;
}

然后我会在我的代码中调用一次initMap() 并完成。

现在我已经在这里阅读了几个问题,并且为地图实现 const-ness 似乎并非易事。

将其设为std::map&lt;std::string, const int&gt; 将不允许我将其填入initMap()。 用非常量临时填充它,并且定义上的复制构造函数也不起作用,因为复制构造函数不容易将非常量版本作为输入。

将其设为const std::map&lt;std::string, int&gt;(我可以在定义期间使用非常量副本填充)将禁止使用[] 运算符进行值访问。

那么有没有办法实现(值)const-ness和初始化结构(最好在头文件中)?

顺便说一句:C++0x、C++11 和 boost:: 都不是一个选项。

【问题讨论】:

  • 您想要“只读的 std:map 数据结构”,还是想要带有只读元素的地图?
  • 我想要前者,但如果这让我成为[] 运营商,我可以接受后者。

标签: c++ dictionary stl constants c++03


【解决方案1】:

您不能使用std::map 可用的insert() 方法吗?

http://www.cplusplus.com/reference/map/map/insert/

编辑:(解决方案)myMap.insert(std::pair&lt;std::string, const int&gt;("Keys", 42));

据我了解,之所以可行,是因为该对的构造函数pair (const first_type&amp; a, const second_type&amp; b) 使用first_typesecond_type 的构造函数初始化其成员firstsecond,采用ab 作为它们各自的参数。

对于您尝试使用的解决方案,我的理解是myMap["Keys"] = 42; 使用int 的默认构造函数初始化map(类型为const int)的成员second。然后尝试为该成员分配一个值。由于这是在 map 类的构造函数之外完成的,因此 const 声明使这不可能。

使用insert() 的解决方案,成员在pair 的构造函数中初始化。因此它们可以被声明为const。将pair复制到map时也是如此。

【讨论】:

  • 扩展您的答案以更加逐字逐句地回答我的问题,我接受是正确的。实际上,使用上面我自己的代码示例,将myMap["Keys"] = 42; 替换为myMap.insert(std::make_pair("Keys", 42)) 将适用于std::map&lt;std::string, const int&gt;。但我不知道为什么这个有效而另一个无效。
  • 我刚刚验证过,它在写访问 (myMap["Keys"] = 23;) 时正确地给了我一个编译器错误,同时仍然允许读访问 (std::cout &lt;&lt; myMap["Keys"])。
  • @Chaos_99 对不起。我更愿意把它写成评论,但我还不允许这样做。
  • @Chaos_99 我为我的答案添加了更深入的解释。这适用于我作为示例给出的解决方案。如果你使用myMap.insert(std::make_pair("Keys", 42)),原理是一样的,但是const int的初始化只在map的构造函数中完成,因为pair的成员类型将是它决定的最适合"Keys"42
【解决方案2】:

虽然这对您来说是不可能的,但其他想要这样做并且拥有 C++11 兼容编译器的人可以使用uniform initialization

std::map<std::string, const int> myMap = {
    { "keys", 42 }
};

哦,顺便说一句,不要在头文件中定义地图。而是在头文件中声明它为extern,然后在源文件中定义它。

【讨论】:

  • 我已经编辑了原始问题。没有 C++0x 也意味着没有 C++11。但是您对声明/定义是绝对正确的。谢谢!
【解决方案3】:

最简单的解决方案是自己编写,包装 标准地图类:

template <typename KeyType, typename MappedType, typename CmpType>
class ConstantMap
{
    typedef std::map<KeyType, MappedType, CmpType> Impl;
    Impl myImpl;
public:
    typedef Impl::value_type value_type;

    template <ForwardIterator>
    ConstantMap( ForwardIterator begin, ForwardIterator end, CmpType cmp = CmpType() )
        : myImpl( begin, end, cmp )
    {
    }

    //  necessary if [] is not going to work for missing keys
    bool contains( KeyType const& key ) const
    {
        return myImpl.find( key ) != myImpl.end();
    }

    MappedType const& operator[]( KeyType const& key ) const
    {
        Impl::const_iterator elem = myImpl.find( key );
        if ( elem == myImpl.end() ) {
            //  Not found, do what you want (maybe throw an exception)
        }
        return elem.second;
    }
};

您可以通过将迭代器传递给序列来初始化地图 任何可以转换为value_type的东西。

根据您的需要,您可能需要添加其他 转发 typedef、函数等。如果你使用 C++11,你 可能还想创建一个可以使用列表的构造函数 初始化器。

【讨论】:

  • 感谢包装类示例代码。我敢肯定,对于许多搜索自定义地图类的人来说,它可能会派上用场。但只要我唯一需要的是 const-ness,我就会采用 @Matzar 的解决方案。
【解决方案4】:

如果map 不能改变,你应该使用const map&lt;string, int&gt;,而不是map&lt;string, const int&gt;:第二个版本允许插入和删除对象。

遗憾的是,您将不得不失去 [] 运算符; C++ 没有ImmutableMap 或类似的东西。不过,std::map::atstd::map::find 还不错……

【讨论】:

  • 如果它在 [] 运算符和插入/删除之间,我会选择插入/删除。不按惯例使用某些东西更容易“卖”给其他开发人员,然后是笨拙的访问语法。
【解决方案5】:

更简单的东西,占用空间更小,速度更快

静态常量 int MyMapData[] = {
    42 // 索引 0 映射到“key”
};

结构 MyMap { const int& operator[](std::string key) const { 开关(键){ 案例“键”:返回 MyMapData[0];

default: return NotANumber; // Return 0 and raise an assertion, or return "Not a Number". } }

};

易于维护,不使用模板,不使用 boost 库并且可以在任何地方编译。

【讨论】:

  • 可能适用于这个简单的示例,但运行带有数千个选项的 switch 语句可能并不比 std::map 访问快。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-03
  • 2011-11-22
  • 1970-01-01
  • 1970-01-01
  • 2014-11-04
  • 2014-06-13
  • 2023-03-10
相关资源
最近更新 更多