Reputation: 786
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
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
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