Reputation: 14563
I'm working on a hash table, and I've got a bucket type that holds my key values, so I can hold them in unions and leave them uninitialized to avoid requiring DefaultConstructible for them:
template <class K, class V>
struct bucket {
bucket() : hash(SENTINEL) {}
bucket(uint64_t hash, K&& k, V&& v)
: hash(hash), key(std::forward(k)), val(std::forward(v)) {}
<copy constructors>
~bucket() {
if (hash != SENTINEL) {
key.~K();
val.~V();
}
}
uint64_t hash;
union { K key; }
union { V key; }
};
My question, then, is something like the = operator, which I would usually write with the copy-and-swap idiom:
bucket& operator =(bucket other) {
using std::swap;
swap(hash, other,hash);
swap(key, other.key);
swap(val, other.val);
}
Is this still safe, even with unitialized key and val?
Upvotes: 1
Views: 227
Reputation: 50053
This fails even earlier: It is UB to read a union
member other than the one last assigned to.
If you have not assigned a value to any member, you also can't read any member, regardless of the type or any "reading uninitalized variables" rules.
As I mentioned in the comments, the correct way to avoid the default constructible requirement would be using placement new.
Upvotes: 5