Передача ссылки std :: pair, взятой из std :: map, в функцию, которая принимает ссылку std :: pair

РЕДАКТИРОВАТЬ: ответ на первый вопрос - использовать std :: pair. Есть идеи по поводу второго (помеченного как «бонусный вопрос»)?

Со следующим кодом:

#include <map>
#include <vector>

void foo(std::pair<int, int>& p)  // EDIT: it needs to be non-const
{}

int main()
{
    std::pair<int, int> p{1,2};
    foo(p);
    std::vector<std::pair<int, int>> v{{1,2}};
    for (auto& element : v)
    {
        foo(element);  // works fine
    }

    std::map<int, int> m{std::make_pair(1,2)};
    //std::map<int, int> m2{{1,2}};
    for (auto& element : m)  // the problematic loop
    {
        foo(element);
    }

    return 0;
}

Я получаю следующее сообщение с m в последнем цикле for:

ошибка: неверная инициализация ссылки типа 'std :: pair &' из выражения типа 'std :: pair'

и следующее с m2 в этом месте:

ошибка: недопустимая инициализация неконстантной ссылки типа 'std :: pair &' из rvalue типа 'std :: pair'

Это почему?

Дополнительный вопрос: я нахожу очень странным, что когда инициализация m2 не закомментирована, а цикл for остается нетронутым (в нем все еще есть m, а m2 никогда не используется), сообщение об ошибке меняется с

ошибка: неверная инициализация ссылки типа 'std :: pair &' из выражения типа 'std :: pair'

to

ошибка: недопустимая инициализация неконстантной ссылки типа 'std :: pair &' из rvalue типа 'std :: pair'

Мне бы хотелось узнать ваши мысли по этому поводу. Я тестировал этот код с помощью onlinegdb.com


person user6646922    schedule 11.07.2018    source источник
comment
Вы не цитировали ошибки дословно. Если бы это было так, были бы упомянуты std::pair<const int, int> и std::pair<int, int>. Несвязанные типы.   -  person StoryTeller - Unslander Monica    schedule 11.07.2018
comment
Я сделал, это действительно говорит только то, что я цитировал   -  person user6646922    schedule 11.07.2018
comment
Тогда позвольте мне порекомендовать компиляторы с более полными сообщениями об ошибках. GCC и Clang дает гораздо лучшую диагностику.   -  person StoryTeller - Unslander Monica    schedule 11.07.2018
comment
Информативное сообщение об ошибке в моем ответе пришло из GCC 6.3 через ideone.com. GDB Online, похоже, использует относительно старый GCC 5.4.1.   -  person Useless    schedule 11.07.2018


Ответы (3)


Согласно документации,

map::value_type = std::pair<const Key, T>

значение

map<int,int>::value_type = pair<const int, int>

Таким образом, компилятор может неявно преобразовать ваш элемент типа pair<const int,int>& во временный (rvalue) типа pair<int,int>, но это не может быть передано в функцию с помощью неконстантной ссылки.

Либо измените тип аргумента на pair<const int,int>, либо возьмите его по значению.


Для справки, gcc 6.3 предоставил эту очень информативную ошибку:

prog.cpp: In function ‘int main()’:
prog.cpp:21:13: error: invalid initialization of non-const reference of type ‘std::pair<int, int>&’ from an rvalue of type ‘std::pair<int, int>’
         foo(element);
             ^~~~~~~
In file included from /usr/include/c++/6/bits/stl_algobase.h:64:0,
                 from /usr/include/c++/6/bits/stl_tree.h:63,
                 from /usr/include/c++/6/map:60,
                 from prog.cpp:1:
/usr/include/c++/6/bits/stl_pair.h:272:19: note:   after user-defined conversion: constexpr std::pair<_T1, _T2>::pair(const std::pair<_U1, _U2>&) [with _U1 = const int; _U2 = int; typename std::enable_if<(std::_PCC<((! std::is_same<_T1, _U1>::value) || (! std::is_same<_T2, _U2>::value)), _T1, _T2>::_ConstructiblePair<_U1, _U2>() && std::_PCC<((! std::is_same<_T1, _U1>::value) || (! std::is_same<_T2, _U2>::value)), _T1, _T2>::_ImplicitlyConvertiblePair<_U1, _U2>()), bool>::type <anonymous> = 1u; _T1 = int; _T2 = int]
         constexpr pair(const pair<_U1, _U2>& __p)
                   ^~~~
prog.cpp:4:6: note:   initializing argument 1 of ‘void foo(std::pair<int, int>&)’
 void foo(std::pair<int, int>& p)

Обратите особое внимание на убедительную подсказку о после пользовательского преобразования ...

person Useless    schedule 11.07.2018
comment
Спасибо, это очень полезно. Вы тоже знаете ответ на второй вопрос? - person user6646922; 11.07.2018
comment
Если вы видите ошибку, которую я вставил выше, это было всегда invalid initialization of non-const reference ... для меня. У меня нет под рукой копии GCC 5.4.1, чтобы понять, почему он делает что-то другое, но это не очевидно. - person Useless; 11.07.2018

Контейнер создает ключ const (это относится ко всем ассоциативным контейнерам):

value_type    std::pair<const Key, T>

Так должно быть void foo(std::pair<int const, int>& p).

person Maxim Egorushkin    schedule 11.07.2018

Вам не разрешено изменять ключи карты, поэтому это работает:

#include <map>
#include <vector>

// foo can modify the first value of the pair
void foo(std::pair<int, int>& p)
{}
// fooConstFirst cannot modify the first value of the pair
void fooConstFirst(std::pair<const int, int>& p)
{}

int main()
{
  std::pair<int, int> p{1,2};
  foo(p);
  std::vector<std::pair<int, int>> v{{1,2}};
  for (auto& element : v)
  {
    foo(element);
  }

  std::map<int, int> m{std::make_pair(1,2)};
  for (auto& element : m)
  {
    fooConstFirst(element);
  }

  return 0;
}
person Olivier Sohn    schedule 11.07.2018