brainchild
brainchild

Reputation: 786

Bind const std::pair<T, U>& to value of std::pair<const T, U>

The below code snippet compiles with a very important warning.

#include <map>
#include <vector>

template <typename iterator>
const std::pair<int, float> &foo(iterator it) {
  return *it;
}

int main() {
  std::vector<std::pair<int, float>> vector;
  std::map<int, float> map;
  vector.push_back(std::make_pair(0, 0.0));
  map.insert(std::make_pair(0, 0.0));
  const std::pair<int, float> &r1 = foo(vector.begin());
  const std::pair<int, float> &r2 = foo(map.begin());
  if (r1 != r2) {
    return 1;
  }
  return 0;
}

There is an implicit conversion from std::pair<const int, float> to std::pair<int, float> during foo(map.begin()) that creates a dangling reference.

ref2.cpp: In instantiation of ‘const std::pair<int, float>& foo(iterator) [with iterator = std::_Rb_tree_iterator<std::pair<const int, float> >]’:
ref2.cpp:16:52:   required from here
ref2.cpp:7:11: warning: returning reference to temporary [-Wreturn-local-addr]
   return *it;
           ^~

We could adjust the type of r2 to std::pair<const int, float> in this case. Nevertheless, it would be useful, in the general case, to assign the results of the two calls to foo() to type-compatible references. For example, the call to foo() might be wrapped in another function that always returns std::pair<int, float>&.

Can the reference assignment be made to operatate in a way that works around the misalignment of const modifiers?

Upvotes: 1

Views: 674

Answers (2)

&#208;аn
&#208;аn

Reputation: 10875

Edit

The question is really about making std::pair<K,V> work with std::pair<const K,V>; vector<> and map<> are red-herrings. (In particular, see the discussion here about why the key in std::map<> is const.)

Better sample code might be:

#include <vector>

template <typename iterator>
const std::pair<const int, float>& bar(iterator it)
{
    return *it;
}

int main()
{
    const std::vector<std::pair<const int, float>> v1{ std::make_pair(0, 0.0f) };
    bar(v1.begin());

    const std::vector<std::pair<int, float>> v2{ std::make_pair(0, 0.0f) };
    bar(v2.begin());

    return 0;
}

According to your comments, what you're really trying to figure out is how to make the std::map<> iterator work like std::vector<>; the result should be a std::pair<> in both cases, not std::pair<const int, ...>.

With that, I've written this hack; I'm sure it's got problems and/or could be improved:

const auto& remove_const(const std::pair<const int, float>& p) {
    return reinterpret_cast<const std::pair<int, float>&>(p); // :-(
}

template <typename iterator>
const std::pair<int, float> &foo(iterator it) {
    return remove_const(*it);
}

Upvotes: 2

marcinj
marcinj

Reputation: 49976

You might change:

template <typename iterator>
const std::pair<int, float> &foo(iterator it) {
  return *it;
}

to:

template <typename iterator>
decltype(auto) foo(iterator it) {
  return *it;
}

this requires c++14, to stay with c++11 use:

auto foo(iterator it) -> decltype(*it) {   

Upvotes: 1

Related Questions