Я создаю дискриминируемый класс, подобный союзу. Я использую С++ 17, поэтому технически я мог бы использовать std::variant
, но из-за конкретного варианта использования я хочу, чтобы значение каждого варианта было более явным (особенно потому, что два случая не содержат данных, кроме того, какой случай они есть). Класс выглядит примерно так (для простоты я буду игнорировать семантику перемещения в вопросе):
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
}
Моя первая мысль об операторе присваивания копии:
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;
}
Мне это кажется прекрасным, но мне интересно, лучше ли стиль С++ вызывать оператор присваивания строки, для которого потребуется что-то вроде этого:
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;
}
Подводя итог, как правильно это сделать (и почему) или это имеет значение?
* Эта строка здесь, потому что моя первоначальная реализация установила aVal
напрямую, не убедившись, что там когда-либо была инициализирована строка, и это привело к сбою.
std::variant
в вашем классе вместоunion
? Вы можете сохранить дополнительное поведение/требования, просто реализуя меньше кода. - person Richard Critten   schedule 06.11.2019std::variant
имеет больше накладных расходов, и, поскольку мой класс действительно довольно прост, это не большая проблема, если только нет какой-то действительно большой и сложной проблемы, которую я действительно не хочу исправлять самостоятельно ( как сstd::shared_ptr
). - person Anonymous   schedule 06.11.2019std::variant
; единственные накладные расходы памяти, которые у него есть, - это одинsize_t
(я полагаю) для индекса типа, что не так много. Эти низкоуровневые классы, такие какstd::variant
, могут быть очень сложными для правильной работы, особенно если вы хотите иметь надлежащую безопасность исключений. - person HolyBlackCat   schedule 07.11.2019