This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Re: Add std::unordered_* C++11 allocator support
- From: Jonathan Wakely <jwakely dot gcc at gmail dot com>
- To: François Dumont <frs dot dumont at gmail dot com>
- Cc: Paolo Carlini <paolo dot carlini at oracle dot com>, "libstdc++ at gcc dot gnu dot org" <libstdc++ at gcc dot gnu dot org>
- Date: Sun, 7 Apr 2013 17:38:10 +0100
- Subject: Re: Add std::unordered_* C++11 allocator support
- References: <51438861 dot 9010207 at gmail dot com> <5145A358 dot 4020104 at oracle dot com> <CAH6eHdQjrkLFa2rNZnq9ndkBs4sgZ4EXgxSWW=LX0kOK4i88Ww at mail dot gmail dot com> <515B41F5 dot 4040207 at gmail dot com> <CAH6eHdRHGv9hFzoiVV3bGZ74UhVfawc+qkiQY9dS5CwCdW1p4A at mail dot gmail dot com>
On 6 April 2013 19:53, Jonathan Wakely wrote:
>
> I'm not very comfortable with the "mutable value type" part which
> accesses the stored value through a pointer to a different type. This
> violates strict aliasing rules and would also give the wrong result if
> users provide an explicit specialization of std::pair<int, Foo> which
> stores pair::second before pair::first, then tries to store that type
> in an unordered_map. When a pair<const int, Foo>* is cast to
> pair<int, Foo>* the user's explicit specialization would not be used
> and the layout would be wrong.
Here's a testcase that fails due to the casting done by _ReuseOrAllocNode
#include <unordered_map>
#include <cassert>
struct Foo {
int data = 0;
};
namespace std {
template<>
struct pair<int, Foo>
{
pair() : second(), first() { }
pair(int i, Foo f) : second(f), first(i) { }
template<typename T, typename U>
pair(const pair<T, U>& p) : second(p.second), first(p.first) { }
template<typename T, typename U,
typename = decltype(std::declval<Foo&>() = std::declval<U&>()),
typename = decltype(std::declval<int&>() = std::declval<T&>())>
pair& operator=(const pair<T, U>& p)
{
first = p.first;
second = p.second;
return *this;
}
Foo second;
int first;
};
}
int main()
{
std::unordered_map<int, Foo> m{ { 1, {} } };
auto mm = m;
m = mm;
assert( m.count(1) == 1 );
}
The map ends up containing {0,1} instead of {1,0}
I believe the code above is valid, if it isn't then it only needs some
additional constructors and operators added to make it meet the
requirements of std::pair, but that wouldn't change the bad behaviour
of the example above.