union members may not have destructors or constructors. So I can’t template the following class Foo on my own MyClass if MyClass has a constructor:
template<class T>
struct Foo {
T val;
Foo(T val_) : val(val_) {}
size_t hash() const {
union {T f; size_t s;} u = { val };
return u.s;
}
};
struct MyClass {
bool a;
double b;
MyClass(bool a_, double b_) : a(a_), b(b_) {}
};
If I do it anyway I get this error:
member 'MyClass Foo<T>::hash() const
[with T = MyClass]::<anonymous union>::f' with constructor
not allowed in union
To get around it I created MyClass with an awkward construction function, which copies the thing around first:
struct MyClass {
bool a;
double b;
};
MyClass createMyClass(bool a, double b) {
MyClass m;
m.a = a;
m.b = b;
return m;
}
But I’m wondering if there is a better way than using this createMyClass function. A constructor would be more efficient, and as a critical component MyClass and Foo<MyClass> are constructed millions of times in my code.
std::pair
I’m also a bit surprised that it is possible to use std::pair in the union:
Foo<std::pair<bool, double> > f2(std::make_pair(true, 3.12));
To my knowledge, std::pair (see code) has a constructor?
EDIT: My original stance on
std::pairwas wrong, it shouldn’t be allowed in a union. For a class to be a valid member of a union it must have a trivial constructor according to standard 9.5.1. The definition of a trivial constructor is this, from paragraph 12.1.5:Paragraph 20.2.2.2 states that the following constructor must be available in a pair:
as soon as this constructor is supplied no default constructor will be implicitly declared.
The funny thing here is that my compiler (Visual Studio 2008) seems to give
std::pairspecial treatment. If I copy the code from thestd::pairimplementation and place it in my own namespace foo the unions don’t work 🙂Your solution is a common way of getting around this problem. I usually prepend the class name with a
C(short for construct) to partially mimic the ordinary constructor syntax, this would in your case becomeCMyClass(a, b).As Steve and Matthieu has pointed out you’re not using a very good hash function though. Firstly there’s no real guarantee (I think, please correct me if I’m wrong) that
fandsin the union will even partially occupy the same memory space, and secondly even if they in practice will probably will share the firstmin(sizeof(s), sizeof(f))bytes this means that forMyClassyou’re only hashing on part of the value. In this case you will hash on the value of thebool a, in this case there’s two options:intas the internal representation for theboolin which case your hash function will only return two values, one for true and one for false.charas the internal representation for thebool. In this case the value will probably be padded to at leastsizeof(int), either with zeroes in which case you have the same situation as 1. or with whatever random data is on the stack whenMyClassis allocated which means you get random hash values for the same input.If you need to hash by the entire value of
TI would copy the data into a temporary buffer like Steve suggests and then use one of the variable-length hash functions discussed here.