首页 > 编程笔记 > C++笔记 阅读:2

C++ std::any的用法(附带实例)

C++ 不像其他语言(如 C# 或 Java)那样具有分层类型系统,因此,它不能像在 .NET 和 Java 中(使用 Object 类型)或像在 JavaScript 中那样在单个变量中存储多个类型的值。

长期以来,开发人员一直使用 void* 来实现这个目的,但这只能帮助我们存储指向任意对象的指针,并不是类型安全的。根据最终目标,替代方案可以选择模板或重载函数。然而,C++17 引入了一个标准的类型安全容器,它被称为 std::any,可以保存任何类型的单个值。

std::any 是基于 boost::any 设计的,在 <any> 头文件中可用。如果你熟悉 boost::any,并且在代码中使用过它,则可以无缝地迁移到 std::any。

C++ std::any使用方式

我们可以使用以下操作来处理 std::any:
1) 要存储值,可以使用构造函数或将值直接赋给 std::any 变量:
std::any value(42);  // integer 42
value = 42.0;       // double 42.0
value = "42"s;      // std::string "42"

2) 要读取值,可以使用非成员函数 std::any_cast():
std::any value = 42.0;

try
{
    auto d = std::any_cast<double>(value);
    std::cout << d << '\n';
}
catch (std::bad_any_cast const & e)
{
    std::cout << e.what() << '\n';
}

3) 要检查存储的值的类型,可以使用成员函数 type():
inline bool is_integer(std::any const & a)
{
    return a.type() == typeid(int);
}

4) 要检查容器是否存储值,可以使用 has_value() 成员函数:
auto ltest = [](std::any const & a) {
    if (a.has_value())
        std::cout << "has value" << '\n';
    else
        std::cout << "no value" << '\n';
};

std::any value;
ltest(value); // no value
value = 42;
ltest(value); // has value

5) 要修改存储的值,可以使用成员函数 emplace()、reset() 或 swap():
std::any value = 42;
ltest(value); // has value
value.reset();
ltest(value); // no value

C++ std::any工作原理

std::any 是一个类型安全的容器,它可以保存任意类型的值,这些类型(更确切地说,是其衰减类型)是可复制构造的。在容器中存储值非常简单,可以使用可用的构造函数(默认构造函数创建不存储值的容器)或赋值操作符。但是,不能直接读取值,需要使用非成员函数 std::any_cast(),该函数将存储的值转换为指定的类型。

如果存储的值的类型与要转换的类型不同,则此函数将抛出 std::bad_any_cast 异常。在隐式可转换类型(如 int 和 long)之间进行强制转换也是不可能的。std::bad_any_cast 派生自 std::bad_cast,因此可以捕获这两种异常类型中的任何一种。

可以使用 type() 成员函数来检查存储的值的类型,该函数返回 type_info 常量引用。如果容器为空,此函数返回 typeid(void)。要检查容器是否存储值,可以使用成员函数 has_value(),如果有值,则返回 true,如果容器为空,则返回 false。

下面的例子展示了如何检查容器是否有值,如何检查存储的值的类型,以及如何从容器中读取值:
void log(std::any const & value)
{
    if (value.has_value())
    {
        auto const & tv = value.type();
        if (tv == typeid(int))
        {
            std::cout << std::any_cast<int>(value) << '\n';
        }
        else if (tv == typeid(std::string))
        {
            std::cout << std::any_cast<std::string>(value) << '\n';
        }
        else if (tv == typeid(
            std::chrono::time_point<std::chrono::system_clock>))
        {
            auto t = std::any_cast<std::chrono::time_point<
                std::chrono::system_clock>>(value);
            auto now = std::chrono::system_clock::to_time_t(t);
            std::cout << std::put_time(std::localtime(&now), "%F %T")
                      << '\n';
        }
        else
        {
            std::cout << "unexpected value type" << '\n';
        }
    }
    else
    {
        std::cout << "(empty)" << '\n';
    }
}

log(std::any{});               // (empty)
log(42);                      // 42
log("42"s);                   // 42
log(42.0);                    // unexpected value type
log(std::chrono::system_clock::now()); // 2016-10-30 22:42:57

如果想存储任意类型的多个值,可以使用标准容器(如 std::vector)来保存类型为 std::any 的值,这里给出了一个例子:
std::vector<std::any> values;
values.push_back(std::any{});
values.push_back(42);
values.push_back("42"s);
values.push_back(42.0);
values.push_back(std::chrono::system_clock::now());

for (auto const v : values)
    log(v);
在这段代码中,名为 values 的 vector 包含 std::any 类型的元素,这些元素又包含 int、std::string、double 和 std::chrono::time_point 值。

相关文章