В целях отладки я писал функцию, которая выполняет итерацию по вектору необязательных переменных любого типа, чтобы проверить, какие из них были инициализированы, но проверка has_value() для всех из них возвращает true, несмотря на то, что некоторым из них никогда не присваивалось значение. их.
Я был бы признателен за любую помощь, указывающую на то, что я неправильно понимаю, поскольку я новичок в С++. Код ниже. Обратите внимание, что когда закомментированная строка раскомментирована, оператор if определяет, что переменная не имеет значения.
#include <iostream>
#include <optional>
#include <any>
bool SimpleCheck(std::vector<std::optional<std::any>> toCheck)
{
bool res = false;
for (int i = 0; i < toCheck.size(); ++i)
{
// toCheck[i] = std::nullopt;
if (!toCheck[i].has_value())
{
std::cout << "item at index " << i << " had no value\n";
res = true;
}
}
return res;
}
int main()
{
std::optional<int> i = 5;
std::optional<std::string> str;
std::optional<double> doub = std::nullopt;
bool check = SimpleCheck({i, str, doub});
std::cout << check << "\n";
return 0;
}
Мой ожидаемый результат:
item at index 1 had no value item at index 2 had no value 1
Фактический результат:
0
Если закомментированная строка раскомментирована, вывод будет таким:
item at index 0 had no value item at index 1 had no value item at index 2 had no value 1
Здесь происходит множество неявных преобразований. Я предполагаю, что ваш std::any инициализируется полным необязательным значением, а затем срабатывает конструктор std::optional, который обертывает тривиальное необязательное (конструктор 8 на этой странице).
Почему вы вообще хотите std::optional<std::any>?





С
std::optional<double> doub = std::nullopt;
std::optional<std::any> a = doub; // it is not a copy constructor
a непустое, но его any пустое std::optional.
Спасибо, это имеет смысл. Знаете ли вы (или кто-либо другой) способ сделать эту проверку правильно, или это невозможно? Я попытался восстановить значение и проверить, имеет ли оно значение, или сравнить значение с std::nullopt, но ни один из них не работает.
Вы хотите проверить, что optional, хранящийся в any, пуст? что-то вроде: if (a) { if (auto* o = std::any_cast<std::optional<double>>(a.get())) { const bool b = o->has_value(); /*..*/ } }?
@ Jarod42, к сожалению, для этого требуется знать, какая специализация std::optional хранится в std::any, просто чтобы проверить, имеет ли она значение.
@PatrickRoberts: это проблема с std::any... std::optional<std::any> i = 5; будет альтернативой (так что у вашего std::any будет int/string).
Мне было бы интересно узнать, можно ли/как вы можете принудительно выбрать № 4 вместо № 8 здесь...
@PatrickRoberts: Для № 4 «Этот конструктор не участвует в разрешении перегрузки», поскольку std::is_constructible_v<std::any, std::optional<T>&> является true.
Все std::optional<std::any> содержат std::any, поэтому ни один из них не является пустым.
Кроме того, все std::any содержат std::optional<T>, поэтому ни один из них не является пустым.
Если вы хотите проверить, содержат ли внутренние optional значения, вам нужно std::any_caststd::any на optional<T>, а затем проверить, имеет ли это значение:
template <class T>
bool test(const std::any& any) {
auto o = std::any_cast<T>(&any);
return o && o->has_value();
}
template <class... Ts>
bool has_optional_value(const std::any& any) {
return (... || test<std::optional<Ts>>(any));
}
bool SimpleCheck(std::vector<std::optional<std::any>> toCheck) {
bool res = false;
for(std::size_t i = 0; i < toCheck.size(); ++i) {
if (!(toCheck[i].has_value() &&
toCheck[i].value().has_value() &&
has_optional_value<int, std::string, double>(toCheck[i].value())))
{
std::cout << "item at index " << i << " had no value\n";
res = true;
}
}
return res;
}
Выход:
item at index 1 had no value
item at index 2 had no value
1
Вместо try...catch я думаю auto ptr = std::any_cast<T>(&any)); return ptr != nullptr && ptr->has_value(); было бы предпочтительнее, не так ли?
@PatrickRoberts Абсолютно! Я обновил его. Спасибо!
Во-первых, optional<any> неэффективен. std::any тоже есть has_value метод.
Затем std::any готов и проглатывает все, что подается, поэтому его построение из std::optional не проверяет необязательный параметр, а затем извлекает значение; он просто поглощает необязательное и содержит необязательное.
Лучшим подходом к вашей проблеме будет то, что я называю кортежем python:
#include <any>
#include <array>
#include <ranges>
#include <format>
#include <vector>
#include <optional>
using py_tuple = std::vector<std::any>;
Давайте воспользуемся сахаром C++20,23:
bool SimpleCheck(py_tuple toCheck)
{
bool res = false;
for( auto&& [i/*integer*/, x/*any&*/]
: toCheck
| std::views::enumerate )
{
if (x.has_value())
continue;
std::cout << std::format("item at index {} had no value\n");
res = true;
}
return res;
}
Далее нам понадобится трансформатор:
auto opt_to_any = []<typename T>
(std::optional<T> const& opt) -> std::any
{
if (opt.has_value()
return {opt.value()};//swallow
else
return {};//default construct
};
Затем мы вызываем тест с вашими входными данными:
bool check = SimpleCheck(
std::array{i, str, doub}
| std::views:: transform(opt_to_any)
| std::ranges::to<std::vector>() );
Мне было лень вызывать opt_to_any, чтобы создать 3 инициализатора для py_tuple; поэтому я закончил тем, что соединил адаптеры диапазонов, чтобы создать гораздо более длинную последовательность с тем же эффектом (я тоже тупой).
std::anyможет быть инициализирован с помощьюstd::optional<T>...