Небезопасный, "noexcept" и без накладной способ доступа к "std:: variant"
std::variant
предоставляет следующие функции доступа:
-
std::get_if
: возьмите указатель на variant
, верните указатель на альтернативу.
template <std::size_t I, typename... Ts>
auto* std::get_if(std::variant<Ts...>* pv) noexcept;
-
Если pv
не является нулевым указателем и pv->index() == I
, возвращает указатель на значение, сохраненное в варианте, на которое указывает pv
. В противном случае возвращает значение нулевого указателя.
Это означает, что реализация get_if
выглядит примерно так:
template <std::size_t I, typename... Ts>
auto* std::get_if(std::variant<Ts...>* pv) noexcept
{
if(pv == nullptr) return nullptr;
if(pv->index() != I) return nullptr;
return &(pv->real_get<I>());
}
-
std::get
: обратитесь к variant
, верните ссылку на альтернативу, throw
на недопустимый доступ.
template <std::size_t I, typename... Ts>
auto& std::get(std::variant<Ts...>& v);
-
Если v.index() == I
, возвращает ссылку на значение, хранящееся в v
. В противном случае выбрасывается std::bad_variant_access
.
Это означает, что реализация get
выглядит примерно так:
template <std::size_t I, typename... Ts>
auto& std::get(std::variant<Ts...>& v)
{
if(v.index() != I) throw std::bad_variant_access{};
return v.real_get<I>();
}
Я хочу функцию небезопасного доступа, которая:
-
Является noexcept
.
-
Делает ссылку на variant
, избегая проверки pv == nullptr
.
-
Поведение undefined, если v.index() != I
.
Почему? Потому что могут быть ситуации, когда я на 100% уверен, что конкретный экземпляр variant
содержит определенный тип кода. Кроме того, было бы полезно при написании общего кода, который уже отдельно проверен v.index() != I
(например, написания моего собственного visit
).
Пример реализации:
template <std::size_t I, typename... Ts>
auto& unsafe_get(std::variant<Ts...>& v)
{
return v.real_get<I>();
}
Есть ли что-то подобное в стандарте? Я не мог найти его. Если нет, можно реализовать для std::variant
, или мне нужно выполнить собственную реализацию variant
?
Ответы
Ответ 1
Как указано @T.C. в комментариях ваша первая и третья просьба несовместимы друг с другом. Это было описано в N3279 под названием "Консервативное использование noexcept в библиотеке".
Существуют по существу два класса контрактов: узкие и широкие. Контракт широкий для функции или операции не определяет поведение undefined.
Такой договор не имеет никаких предварительных условий. В стандартной библиотеке отмечены noexcept
только функции с широкими контрактами.
OTOH, контракт узкий - это контракт, который не является широким. Узкие контракты для функций или операций приводят к undefined поведению при вызове таким образом, который нарушает документированный контракт. Они не могут быть отмечены noexcept
. Вместо этого лучшее, что вы можете ожидать, это то, что они задокументированы как "Броски: ничего".
Похоже, что вам не повезло, и в текущих предложениях для std::variant
такой беспрепятственный доступ не предоставляется.
Ответ 2
Думаю, вам нужно реализовать весь вариант самостоятельно.
Хотя неограниченные союзы могут быть полезны - они, по крайней мере, решают ставить несколько типов в одном и том же месте с проблемой обработки выравнивания.