In the comments to this answer, Koushik raised a very valid point.
Take the following:
union U
{
int x;
const T y;
};
(I choose T such that there is no common initial sequence of layout compatibility here, meaning only one member may be active at any given time per [C++11: 9.5/1].)
Since only one member may be "active" at any one time (made active by writing to it), and y cannot be written to after initialisation, isn't this rather pointless? I mean, y can only be read from until the first time x is written to, and at that only if y was the initialised member.
Is there some use case I'm missing? Or is this indeed a pretty pointless confluence of language features?
(This has been mentioned before)
Here's a contrived example of a reference-semantics type where you'd only want to grant const access to. The union is used in a variant-like data type returned from a "type-erasing" function.
#include <memory>
template<class T>
struct reference_semantics
{
public:
reference_semantics(T* p ) : m(p) {}
int observe() const { return *m; }
void change(T p) { *m = p; }
private:
T* m;
};
struct variant
{
enum T { INT, DOUBLE } type;
union U
{
reference_semantics<int> const i;
reference_semantics<double> const d;
U(int* p) : i(p) {}
U(double* p) : d(p) {}
} u;
};
#include <iostream>
std::ostream& operator<<(std::ostream& o, variant const& v)
{
switch(v.type)
{
case variant::INT:
return o << "INT: "<<v.u.i.observe();
case variant::DOUBLE:
return o << "DOUBLE: "<<v.u.d.observe();
}
}
#include <string>
variant type_erased_access(std::string name)
{
// imagine accesses to a map or so
static double dval = 42.21;
static int ival = 1729;
if(name == "Lightness") return { variant::DOUBLE, &dval };
else return { variant::INT, &ival };
}
int main()
{
variant v0( type_erased_access("Lightness") );
std::cout << v0 << "\n";
variant v1( type_erased_access("Darkness") );
std::cout << v1 << "\n";
}
Imagine now that instead of int and double, much larger data types are used, and that the reference_semantics data type actually provides more functionality than just returning the value.
It might even be possible that you want to return a reference_semantics<some_type> const for some arguments, but a plain int for others. In that case, your union might even have const and non-const members.
It does have uses:
1) For offering a const_cast-like technique. In a sense, x = const_cast<...>(y).
2) When dealing with templates, sometimes you need a const version of a data type so you match other parameter types.
(I've seen (1) used when programming against legacy interfaces).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With