【问题标题】:How to apply transform to an STL map in C++如何在 C++ 中将转换应用于 STL 映射
【发布时间】:2011-12-14 07:49:30
【问题描述】:

在 C++ 中,我使用转换将映射的所有值更改为大写。

  std::map<std::string, std::string> data = getData();

  // make all values uppercase
  std::transform(data.begin(), data.end(), data.begin(),
         [](std::pair<std::string, std::string>& p) {
           boost::to_upper(p.second);
           return(p);
         });

这给了我以下编译错误:

/opt/local/include/gcc46/c++/bits/stl_algo.h:4805:2: error: no match for call to '(main(int, char**)::<lambda(std::pair<std::basic_string<char>, std::basic_string<char> >&)>) (std::pair<const std::basic_string<char>, std::basic_string<char> >&)

我认为我的 lambda 表达式中的参数类型有问题。这可能很简单,但我似乎无法弄清楚预期的结果。

【问题讨论】:

  • 而不是假设容器存储特定类型。您可以通过 value_type 访问类型信息。 std::map&lt;std::string, std::string&gt;::value_type

标签: c++ stl map lambda transform


【解决方案1】:

您在该对的第一种类型中缺少 const。

[](std::pair<const std::string, std::string>& p) {

但这不是你的问题:你不能使用 map 作为 OutputIterator,因为它们不支持赋值。但是,您可以使用 std::for_each 改变第二个参数。

好老map_to_foobar

std::for_each(data.begin(), data.end(), 
              [](std::pair<const std::string, std::string>& p) {
                p.second = "foobar";
              });

概念性的东西:使用与输入和输出相同的范围调用transform 是非常合法的,如果你的所有仿函数都按值返回并且不改变它们的参数,那么它就很有意义。然而,在原地改变某些东西可能会更快(或者至少在代码中看起来更快,更不用说优化编译器了)并且对成员函数很有意义。

【讨论】:

  • 添加一个常量给了我另一个错误 /opt/local/include/gcc46/c++/bits/stl_pair.h:156:2: 错误:传递 'const std::basic_string' 作为'std::basic_string<_chart _traits _alloc>& std::basic_string<_chart _traits _alloc>::operator=(const std::basic_string<_chart _traits _alloc>&) 的 'this' 参数 [with _CharT = char, _Traits = std::char_traits, _Alloc = std::allocator, std::basic_string<_chart _traits _alloc> = std::basic_string]' 丢弃限定符 [-fpermissive ]
  • 或者,您可以将std::transform 与转换迭代器一起使用。例如,我在an answer to another question 中发布的key_iterator 可以简单地转换为value_iterator。然后你会得到std::transform(begin_values(data), end_values(data), begin_values(data), [](std::string s) { boost::to_upper(s); return s; });。在这种情况下,我建议std::for_each,因为它消除了不必要的副本。
  • Boost 提供了范围适配器boost::adaptors::map_values,它可以解决问题。当然包括不必要的副本。
【解决方案2】:

如果你打算坚持std::transform,那么你需要std::inserter()

C++03 MCVE

typedef std::map<int, std::string> Map;

struct ToUpper
{
    Map::value_type & operator()(Map::value_type & pair) const
    {
        boost::to_upper(pair.second);
        return pair;
    }
};

int main()
{
    Map m;
    m[0] = "snake_case";
    m[1] = "camelCase";
    m[2] = "PascalCase";
    
    std::transform(m.begin(), m.end(), std::inserter(m, m.end()), ToUpper());
    
    for (Map::const_iterator it = m.begin(); it != m.end(); ++it)
        std::cout << it->first << ", " << it->second << std::endl;
}

C++11(真的可以在main() 中做任何事情)

int main()
{
    auto m = getData();

    auto toUpper = [] (decltype(m)::value_type & pair)
    {
        boost::to_upper(pair.second);
        return pair;
    };

    std::transform(m.begin(), m.end(), std::inserter(m, m.end()), toUpper);

    for (auto const & pair : m)
        std::cout << pair.first << ", " << pair.second << std::endl;
}

C++14(可以在 lambda 参数中使用auto

int main()
{
    auto m = getData();

    auto toUpper = [] (auto & pair)
    {
        boost::to_upper(pair.second);
        return pair;
    };
    std::transform(m.begin(), m.end(), std::inserter(m, m.end()), toUpper);

    for (auto const & pair : m)
        std::cout << pair.first << ", " << pair.second << std::endl;
}

C++17(只是因为我喜欢结构化绑定)

int main()
{
    auto m = getData();

    auto toUpper = [] (auto & pair)
    {
        boost::to_upper(pair.second);
        return pair;
    };
    std::transform(m.begin(), m.end(), std::inserter(m, m.end()), toUpper);

    for (auto const & [key, value] : m)
        std::cout << key << ", " << value << std::endl;
}

【讨论】:

    猜你喜欢
    • 2012-06-11
    • 2011-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-06
    • 2020-08-17
    • 1970-01-01
    相关资源
    最近更新 更多