I'm creating a discriminated union-like class. I am using c++ 17, so I could technically use a std::variant
, but because of the specific use-case I want the meaning of each variant to be more explicit (especially because two of the cases hold no data except which case they are). The class looks something like this (for the purposes of simplicity, I'll ignore move semantics in the question):
class MyC {
public:
enum class Kind {A, B, C, D};
private:
Kind _kind;
union {
struct {} _noVal;
string _aVal;
int _bVal;
};
MyC(Kind kind) : _kind(kind), _noVal() {}
public:
MyC(const MyC& other) : _kind(other.kind), _noVal() {
if (_kind == Kind::A) new (&_aVal) string(other._aVal);
if (_kind == Kind::B) _bVal = other._bVal;
}
~MyC() {
if (_kind == Kind::A) _aVal.~string();
}
MyC& operator =(const MyC&);
// factory methods and methods for consuming the current value
}
My first thought for the copy assignment operator is
MyC& MyC::operator &(const MyC& other) {
this->~MyC();
_kind = other._kind;
if (_kind == Kind::A) new (&_aVal) string(other.aVal);
else if (_kind == Kind::B) _bVal = other.bVal;
else _noVal = other.noVal;
return *this;
}
This seems fine to me, but I'm wondering if it's better c++ style to call string's copy assignment operator, which would require something more like this:
MyC& MyC::operator &(const MyC& other) {
if (other._kind == Kind::A) {
if (_kind != Kind::A) new (&_aVal) string; // *
_aVal = other.aVal;
} else if (other._kind == Kind::B) {
_bVal = other.bVal;
} else {
_noVal = other.noVal;
}
_kind = other._kind;
return *this;
}
To summarize, what is the right way to do this (and why), or does it matter?
* This line is here because my original implementation set aVal
directly without making sure a string had ever been initialized there, and it crashed.