在std :: map中的非现有项上返回operator []的值

时间:2014-09-30 11:46:09

标签: c++ return-value stdmap

在构建基于std :: map的容器时,我遇到了意外行为:检索非现有键的值并不提供使用默认构造函数构造的新对象。

我在这里缺少什么?

简化的测试用例程序:

#include <cstdio>
#include <algorithm>
#include <map>
#include <string>


static std::string to_lower(const std::string& str)
{
    std::string lower_label;
    std::transform(str.begin(), str.end(), lower_label.begin(), ::tolower);
    return lower_label;
}

class Int {
   public:
      Int () : i(0) { }
      Int (int _i) : i(_i) { }

      int val() const { return i; }

   private:
      int i;
};

std::map<std::string, Int> ints;

Int GetInt(const std::string& label)
{
    std::string lower_label = to_lower(label);
    return ints[lower_label];
}

void AddInt(Int image, const std::string& label)
{
    std::string lower_label = to_lower(label);
    ints[lower_label] = image;
}

int main()
{
    Int k;
    printf ("default Int: %d\n", k.val());

    AddInt(Int(5), "I5");
    Int i = GetInt("i5");
    printf ("existing Int: %d\n", i.val());

    Int j = GetInt("LaLa");
    printf ("non-existing Int:  %d\n", j.val());  // expecting 0

    return 0;
}

输出:

default Int: 0
existing Int: 5
non-existing Int:  5

2 个答案:

答案 0 :(得分:3)

您的transform写入空字符串,因此程序具有未定义的行为。

在实践中发生的事情是lower_label在返回时仍然长度为零,所以每次调用to_lower时都会得到相同的键:空字符串。这意味着您的地图有一个条目,每次调用GetInt都会返回相同的条目。

但是,由于您有未定义的行为,您很幸运,它不会崩溃或擦除您的磁盘。

在尝试写入之前,您需要使用back_insert_iterator或正确设置字符串大小。

或者:

  std::string lower_label;
  std::transform(str.begin(), str.end(), std::back_inserter(lower_label), ::tolower);

或者:

  std::string lower_label;
  lower_label.resize(str.size());
  std::transform(str.begin(), str.end(), lower_label.begin(), ::tolower);

答案 1 :(得分:2)

更改您的to_lower功能(您也不需要它static),以便它可以放置小写字符:

std::string to_lower(const std::string& str)
{
    std::string lower_label;
    std::transform(str.begin(), str.end(), std::back_inserter(lower_label), ::tolower);
    return lower_label;
}

然后没关系: 默认Int:0 现有的Int:5 不存在的Int:0