Как применить преобразование к карте STL в 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> >&)

Я думаю, что что-то не так с типом аргумента в моем лямбда-выражении. Вероятно, это что-то простое, но я не могу понять, чего ожидать.


person daj    schedule 24.10.2011    source источник
comment
Вместо того, чтобы предполагать, что контейнер хранит определенный тип. Вы можете получить доступ к информации о типе через value_type. std::map<std::string, std::string>::value_type   -  person Martin York    schedule 24.10.2011


Ответы (2)


Вам не хватает 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 с тем же диапазоном, что и для ввода и вывода, вполне законен и имеет большой смысл, если все ваши функторы возвращают значение и не мутируют свои аргументы. Однако изменение чего-то на месте может быть быстрее (или, по крайней мере, выглядеть быстрее в коде, не говоря уже об оптимизирующем компиляторе) и имеет большой смысл с функциями-членами.

person pmr    schedule 24.10.2011
comment
Добавление константы дает мне еще одну ошибку /opt/local/include/gcc46/c++/bits/stl_pair.h:156:2: ошибка: передача «const std::basic_string‹char›» в качестве «этого» аргумента «std: :basic_string‹_CharT, _Traits, _Alloc›& std::basic_string‹_CharT, _Traits, _Alloc›::operator=(const std::basic_string‹_CharT, _Traits, _Alloc›&) [с _CharT = char, _Traits = std: :char_traits‹char›, _Alloc = std::allocator‹char›, std::basic_string‹_CharT, _Traits, _Alloc› = std::basic_string‹char›]' отбрасывает квалификаторы [-fpermissive] - person daj; 24.10.2011
comment
В качестве альтернативы вы можете использовать std::transform с итератором преобразования. Например, мой 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, потому что он устраняет ненужную копию. - person James McNellis; 24.10.2011
comment
Boost предлагает адаптер диапазона boost::adaptors::map_values, который помогает. Включая ненужную копию, конечно. - person Brandlingo; 07.02.2019

Если вы планируете придерживаться std::transform, вам нужно std::inserter():

С++ 03 MVCE

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++11main() действительно можно делать все)

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 (вы можете использовать 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;
}
person Chnossos    schedule 18.06.2020