Недавно я следил за обсуждением Reddit, которое привело к хорошему сравнению std::visit
оптимизации по компиляторам. Я заметил следующее: https://godbolt.org/z/D2Q5ED
И GCC9, и Clang9 (я полагаю, они используют один и тот же stdlib) не генерируют код для проверки и создания бесполезного исключения, когда все типы удовлетворяют некоторым условиям. Это приводит к улучшению кода, поэтому я поднял проблему с MSVC STL и получил следующий код:
template <class T>
struct valueless_hack {
struct tag {};
operator T() const { throw tag{}; }
};
template<class First, class... Rest>
void make_valueless(std::variant<First, Rest...>& v) {
try { v.emplace<0>(valueless_hack<First>()); }
catch(typename valueless_hack<First>::tag const&) {}
}
Утверждалось, что это делает любой вариант бесполезным, и, читая документ, он должен:
Во-первых, уничтожает текущее значение (если оно есть). Затем выполняется прямая инициализация содержащегося значения, как если бы оно создавало значение типа
T_I
с аргументами.std::forward<Args>(args)....
Если выброшено исключение,*this
может стать valueless_by_exception.
Что я не понимаю: почему это называется «май»? Законно ли оставаться в старом состоянии, если вся операция скинута? Потому что это то, что делает GCC:
// For suitably-small, trivially copyable types we can create temporaries
// on the stack and then memcpy them into place.
template<typename _Tp>
struct _Never_valueless_alt
: __and_<bool_constant<sizeof(_Tp) <= 256>, is_trivially_copyable<_Tp>>
{ };
И позже он (условно) делает что-то вроде:
T tmp = forward(args...);
reset();
construct(tmp);
// Or
variant tmp(inplace_index<I>, forward(args...));
*this = move(tmp);
Следовательно, в основном это создает временное и, если это удается, копирует / перемещает его в реальное место.
ИМО это нарушение "Во-первых, уничтожает текущее значение", как указано в документе. Когда я прочитал стандарт, то после a v.emplace(...)
текущее значение в варианте всегда уничтожается, и новый тип является либо установленным типом, либо не имеет значения.
Я понимаю, что условие is_trivially_copyable
исключает все типы, которые имеют наблюдаемый деструктор. Так что это также может быть, как: «как-будто вариант повторно инициализируется со старым значением» или около того. Но состояние варианта является наблюдаемым эффектом. Так действительно ли стандарт позволяет, что emplace
не меняет текущее значение?
Изменить в ответ на стандартную цитату:
Затем инициализирует содержащееся в нем значение, как если бы он выполнял прямую инициализацию без списка значений типа TI с аргументами
std::forward<Args>(args)...
.
T tmp {std::forward<Args>(args)...}; this->value = std::move(tmp);
Действительно ли это считается верной реализацией вышесказанного? Это то, что подразумевается под «как будто»?
might/may
формулировкой, поскольку в стандарте не указывается, что является альтернативой.