user6646922
user6646922

Reputation: 517

Passing an std::pair reference taken from an std::map to a function that accepts an std::pair refererence

EDIT: The answer to the first question is to use std::pair. Any ideas about the second one (marked as 'bonus question')?

With the following code:

#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;
}

I get the following message with m in the latter for loop:

error: invalid initialization of reference of type 'std::pair&' from expression of type 'std::pair'

and the following with m2 in that place:

error: invalid initialization of non-const reference of type 'std::pair&' from an rvalue of type 'std::pair'

Why is that?

Bonus question: I find it very peculiar that when the initialization of m2 is not commented out and the for loop stays untouched (there's still m in it and m2 is never used) the error message changes from

error: invalid initialization of reference of type 'std::pair&' from expression of type 'std::pair'

to

error: invalid initialization of non-const reference of type 'std::pair&' from an rvalue of type 'std::pair'

I'd love to know your thoughts about it. I tested this code with onlinegdb.com

Upvotes: 0

Views: 844

Answers (3)

Olivier Sohn
Olivier Sohn

Reputation: 1322

You're not allowed to modify the keys of the map, hence this works:

#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;
}

Upvotes: 3

Useless
Useless

Reputation: 67733

Per the documentation,

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

meaning

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

So the compiler can implicitly convert your element of type pair<const int,int>& to a temporary (rvalue) of type pair<int,int>, but this can't be passed by non-const ref to the function.

Either change the argument type to pair<const int,int>, or take it by value.


For reference, gcc 6.3 provided this very informative error:

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)

Note particularly the strong clue about after user-defined conversion...

Upvotes: 4

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136266

The container makes the key const (this applies to all associative containers):

value_type    std::pair<const Key, T>

So it should be void foo(std::pair<int const, int>& p).

Upvotes: 6

Related Questions