Почему std::for_each на карте вызывает конструктор копирования?

У меня есть следующий простой пример, в котором я хочу вызвать std::for_each для коллекции некопируемых объектов:

class A {
public:
    A() : x(0) {}
    A(const A&) = delete;

private:
    int x;
};

void func() {
    std::vector<A> v(10);
    std::map<int, A> m;

    // works as expected
    std::for_each(begin(v), end(v), [](const A& a) { /* do nothing */ });

    // error calling copy constructor
    std::for_each(begin(m), end(m), [](const std::pair<int, A>& a) { /* do nothing */ });
}

Если я помещу все в std::vector, все будет работать, как я и ожидал, но при использовании std::map вдруг std::for_each захочет вызвать (удаленный) конструктор копирования. Почему? Я бы предположил, что я просто получаю ссылку на пару, которая сохраняется в карте, без каких-либо необходимых копий.


person fschoenm    schedule 09.01.2014    source источник
comment
@fschoenm смотрите ответ или ответы в связанном, очень похожем вопросе   -  person Hulk    schedule 09.01.2014
comment
Спасибо, на мой вопрос ответили. Я не нашел другой пост, потому что я искал for_each вместо foreach, и поисковая система не показала другого результата.   -  person fschoenm    schedule 09.01.2014


Ответы (1)


Проблема в том, что std::map имеет тип внутреннего значения std::pair<const Key, Value>. Вместо того, чтобы явно указывать это, контейнеры стандартной библиотеки позволяют вам извлечь это из типа контейнера:

В С++ 11 выполните (то же, что и в С++ 98, но вам придется использовать объект функции, а не лямбду внутри for_each, а также использовать typedef вместо using =):

using value_type = std::map<int, A>::value_type;
std::for_each(begin(m), end(m), [](value_type const& a) { /* do nothing */ });

In C++14 do:

std::for_each(begin(m), end(m), [](auto const& a) { /* do nothing */ });

Использование auto внутри лямбда-выражения поддерживается Clang 3.4, ноябрьской CTP-версией Visual Studio 2013 и GCC 4.9.

person TemplateRex    schedule 09.01.2014
comment
Лямбда в С++ 03 будет толкать его :) Я думаю, вы имеете в виду С++ 11 и С++ 1y соответственно. - person juanchopanza; 09.01.2014
comment
Спасибо! Я знал, что у std::map теперь есть постоянный ключ. Я просто не установил соединение, и мой компилятор был менее чем полезен с его сообщениями об ошибках. - person fschoenm; 09.01.2014
comment
Также обратите внимание, что std::for_each(begin(m), end(m), [](std::pair<int, A const&> a) { /* do nothing */ }) должно работать: скопируйте ключ, но возьмите ссылку на значение. - person Yakk - Adam Nevraumont; 15.01.2014
comment
@Yakk верно, но для универсального кода явные типы более подробные, а копирование + ссылка стоит дорого (например, если ключ std::string) - person TemplateRex; 16.01.2014
comment
Может ли кто-нибудь исправить опечатку формы в from. - person Herbert; 03.01.2015
comment
@Herbert, спасибо, исправлено! - person TemplateRex; 03.01.2015