Взаимодействие между std::Optional<std::any> и has_value()

В целях отладки я писал функцию, которая выполняет итерацию по вектору необязательных переменных любого типа, чтобы проверить, какие из них были инициализированы, но проверка 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<T>...
Jarod42 19.08.2023 23:30

Здесь происходит множество неявных преобразований. Я предполагаю, что ваш std::any инициализируется полным необязательным значением, а затем срабатывает конструктор std::optional, который обертывает тривиальное необязательное (конструктор 8 на этой странице).

Silvio Mayolo 19.08.2023 23:32

Почему вы вообще хотите std::optional<std::any>?

n. m. could be an AI 20.08.2023 02:26
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
52
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

С

std::optional<double> doub = std::nullopt;
std::optional<std::any> a = doub; // it is not a copy constructor

a непустое, но его any пустое std::optional.

Спасибо, это имеет смысл. Знаете ли вы (или кто-либо другой) способ сделать эту проверку правильно, или это невозможно? Я попытался восстановить значение и проверить, имеет ли оно значение, или сравнить значение с std::nullopt, но ни один из них не работает.

Daniel Tyebkhan 19.08.2023 23:45

Вы хотите проверить, что optional, хранящийся в any, пуст? что-то вроде: if (a) { if (auto* o = std::any_cast<std::optional<double>>(a.get())) { const bool b = o->has_value(); /*..*/ } }?

Jarod42 20.08.2023 00:05

@ Jarod42, к сожалению, для этого требуется знать, какая специализация std::optional хранится в std::any, просто чтобы проверить, имеет ли она значение.

Patrick Roberts 20.08.2023 00:09

@PatrickRoberts: это проблема с std::any... std::optional<std::any> i = 5; будет альтернативой (так что у вашего std::any будет int/string).

Jarod42 20.08.2023 00:23

Мне было бы интересно узнать, можно ли/как вы можете принудительно выбрать № 4 вместо № 8 здесь...

Patrick Roberts 20.08.2023 00:27

@PatrickRoberts: Для № 4 «Этот конструктор не участвует в разрешении перегрузки», поскольку std::is_constructible_v<std::any, std::optional<T>&> является true.

Jarod42 20.08.2023 01:08

Все 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(); было бы предпочтительнее, не так ли?

Patrick Roberts 20.08.2023 00:51

@PatrickRoberts Абсолютно! Я обновил его. Спасибо!

Ted Lyngmo 20.08.2023 00:54

Во-первых, 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; поэтому я закончил тем, что соединил адаптеры диапазонов, чтобы создать гораздо более длинную последовательность с тем же эффектом (я тоже тупой).

Другие вопросы по теме