Преобразовать `std :: any` в` std :: option` - PullRequest
0 голосов
/ 13 апреля 2020

С std::any не нужно ничего делать (кроме как хранить его), пока его содержащийся тип не станет известным или подозреваемым. Затем его можно запросить (type()) или разыграть (any_cast). Но что делать, когда вместо одного типа необходимо иметь дело с несколькими типами? В этом случае решением может быть преобразование его в std::variant.

Например, API предоставляет std::any объектов, но требуется только конечный набор типов, и объекты должны храниться в контейнере ( vector, tree et c.).

Как преобразовать std::any в std::variant?


Отказ от ответственности: std::any в основном предназначен для использования в библиотеке код, целью которого является замена void * в некоторых умных шаблонах и стирание типов. Как и в случае с любой новой вещью, std::any может быть злоупотреблен или использован неправильно. Пожалуйста, подумайте дважды, является ли std::any правильным решением для вашего кода.

1 Ответ

3 голосов
/ 13 апреля 2020

Этот код принимает объект std::any вместе со списком типов и преобразует объект в std::variant или выдает std::bad_any_cast, если сохраненный тип не относится к одному из указанных типов.

#include <any>
#include <variant>
#include <optional>
#include <typeinfo>

template <class... Args>
auto any_to_variant_cast(std::any a) -> std::variant<Args...>
{
    if (!a.has_value())
        throw std::bad_any_cast();

    std::optional<std::variant<Args...>> v = std::nullopt;

    bool found = ((a.type() == typeid(Args) && (v = std::any_cast<Args>(std::move(a)), true)) || ...);

    if (!found)
        throw std::bad_any_cast{};

    return std::move(*v);
}

Пример использования:

auto test(const std::any& a)
{
    auto v = any_to_variant_cast<int, std::string>(a);

    std::visit([](auto val) { std::cout << val << std::endl; }, v);
}

Код на Godbolt


Некоторые пояснения:

std::optional<std::variant<Args...>> используется, потому что std::variant<Args...> конструктор по умолчанию создает вариант, содержащий инициализированное значением первой альтернативы, и требует, чтобы первая альтернатива была конструируемой по умолчанию.

   ((a.type() == typeid(Args) && (v = std::any_cast<Args>(std::move(a)), true)) || ...)
//   ------------------------     -------------------------------------  
//          type_check                             any_cast            

Это выражение сгиба, Я переименовал некоторые подвыражения, чтобы их было легче объяснить. При переименовании выражение становится:

// ((type_check && (any_cast, true)) || ...)
  • , если type_check равно false, то:
    • (any_cast, true) не оценивается из-за короткого замыкания &&
    • (type_check && (any_cast, true)) оценивается как false
    • , следующий оператор в выражении сгиба оценивается
  • , если type_check равно true, то :
    • (any_cast, true) оценивается:
      • any_cast оценивается. Вариант получает значение из любого объекта.
      • any_cast результат отбрасывается
      • true оценивается
      • (any_cast, true) оценивается как true
    • (type_check && (any_cast, true)) оценивается как true
    • остальная часть сгиба не оценивается из-за короткого замыкания ||
    • всего выражения ( сгиб) оценивается как true
  • , если нет type_check, оценивается как true, тогда все выражение (сгиб) оценивается как false
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...