c++17中的any

一、类型存储的处理

在许多语言中,做为初学者都会有这种想法,为啥要有那么多的类型,就不能一个类型把它们都包含了,到用得时候儿自动转换为想到的类型或者再手动转换成想到的类型。这玩意儿确实是个好想法,但又是一个不太切合实际的想法。对计算机的要求性能很高,而高性能就需要对一些判断的语句要少,是啥就应该是啥,而不是用到了再去分析。但这个想法用在某些场景上又确实是一个好主意。所以在有一些语言上,对此也有了些支持。
比如rust的Any模块,还有就是c++17中的std::any.

二、std::any是什么

类型转换最需要什么?一个是准确的类型判断和获取;一个是类型名称的获取,有了这两个,就可以比较顺利进行类型的转换了。而为了实现这两点就需要有一个容器来提供这种实现。而在c++17中提供了std::any这个类。在标准库的文档中可以看到它的基本的函数,会发现最主要的函数有三个“operator=”,“type”,“any_cast”,前两个是成员函数,后面这个是非成员函数,通过字面意思都可以明白是什么意思了。
std::any在文档中的描述是:
“The class any describes a type-safe container for single values of any copy constructible type.

  1. An object of class any stores an instance of any type that satisfies the constructor requirements or is empty, and this is referred to as the state of the class any object. The stored instance is called the contained object. Two states are equivalent if they are either both empty or if both are not empty and if the contained objects are equivalent.
  2. The non-member any_cast functions provide type-safe access to the contained object.
    Implementations are encouraged to avoid dynamic allocations for small objects, but such an optimization may only be applied to types for which std::is_nothrow_move_constructible returns true.”
    这段代码其实是对基础的创建和判断状态等价进行了说明;同时,对转换时的要求指定了明确的结果。

三、应用

看一下官方的例程:

#include <algorithm>
#include <any>
#include <iostream>
#include <string>
#include <vector>
 
class Star
{
    std::string name;
    int id;
 
public:
    Star(std::string name, int id) : name { name }, id { id }
    {
        std::cout << "Star::Star(string, int)\n";
    }
 
    void print() const
    {
        std::cout << "Star{ \"" << name << "\" : " << id << " };\n";
    }
};
 
auto main() -> int
{
    std::any celestial;
    // (1) emplace( Args&&... args );
    celestial.emplace<Star>("Procyon", 2943);
    const auto* star = std::any_cast<Star>(&celestial);
    star->print();
 
    std::any av;
    // (2) emplace( std::initializer_list<U> il, Args&&... args );
    av.emplace<std::vector<char>>({ 'C', '+', '+', '1', '7' } /* 无参数 */ );
    std::cout << av.type().name() << '\n';
    const auto* va = std::any_cast<std::vector<char>>(&av);
    std::for_each(va->cbegin(), va->cend(), [](char const& c) { std::cout << c; });
    std::cout << '\n';
}

再看一个type函数:

#include <type_traits>
#include <any>
#include <functional>
#include <iomanip>
#include <iostream>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <vector>
 
template<class T, class F>
inline std::pair<const std::type_index, std::function<void(std::any const&)>>
    to_any_visitor(F const &f)
{
    return {
        std::type_index(typeid(T)),
        [g = f](std::any const &a)
        {
            if constexpr (std::is_void_v<T>)
                g();
            else
                g(std::any_cast<T const&>(a));
        }
    };
}
 
static std::unordered_map<
    std::type_index, std::function<void(std::any const&)>>
    any_visitor {
        to_any_visitor<void>([]{ std::cout << "{}"; }),
        to_any_visitor<int>([](int x){ std::cout << x; }),
        to_any_visitor<unsigned>([](unsigned x){ std::cout << x; }),
        to_any_visitor<float>([](float x){ std::cout << x; }),
        to_any_visitor<double>([](double x){ std::cout << x; }),
        to_any_visitor<char const*>([](char const *s)
            { std::cout << std::quoted(s); }),
        // ……添加更多你的类型的特化……
    };
 
inline void process(const std::any& a)
{
    if (const auto it = any_visitor.find(std::type_index(a.type()));
        it != any_visitor.cend()) {
        it->second(a);
    } else {
        std::cout << "Unregistered type "<< std::quoted(a.type().name());
    }
}
 
template<class T, class F>
    inline void register_any_visitor(F const& f)
{
    std::cout << "Register visitor for type "
              << std::quoted(typeid(T).name()) << '\n';
    any_visitor.insert(to_any_visitor<T>(f));
}
 
auto main() -> int
{
    std::vector<std::any> va { {}, 42, 123u, 3.14159f, 2.71828, "C++17", };
 
    std::cout << "{ ";
    for (const std::any& a : va) {
        process(a);
        std::cout << ", ";
    }
    std::cout << "}\n";
 
    process(std::any(0xFULL)); //< 反注册 "y" 的类型( unsigned long long )
    std::cout << '\n';
 
    register_any_visitor<unsigned long long>([](auto x) {
        std::cout << std::hex << std::showbase << x; 
    });
 
    process(std::any(0xFULL)); //< OK : 0xf
    std::cout << '\n';
}

这里面有一个小细节,typeid这个运算符typeid(var).name(),未必返回“int”"double"啥的,所以需要需要自己作一下单独的判断,诸如“typeid(var)==typeid(int)”,下面再看一个any_cast这个函数:

#include <string>
#include <iostream>
#include <any>
#include <utility>
 
int main()
{
    // 简单示例
 
    auto a = std::any(12);
 
    std::cout << std::any_cast<int>(a) << '\n'; 
 
    try {
        std::cout << std::any_cast<std::string>(a) << '\n';
    }
    catch(const std::bad_any_cast& e) {
        std::cout << e.what() << '\n';
    }
 
    // 指针示例
 
    if (int* i = std::any_cast<int>(&a)) {
       std::cout << "a is int: " << *i << '\n';
    } else if (std::string* s = std::any_cast<std::string>(&a)) {
       std::cout << "a is std::string: " << *s << '\n';
    } else {
       std::cout << "a is another type or unset\n";
    }
 
    // 进阶示例
 
    a = std::string("hello");
 
    auto& ra = std::any_cast<std::string&>(a); //< 引用
    ra[1] = 'o';
 
    std::cout << "a: "
        << std::any_cast<const std::string&>(a) << '\n'; //< const 引用
 
    auto b = std::any_cast<std::string&&>(std::move(a)); //< 右值引用
 
    // 注意: 'b' 是移动构造的 std::string , 'a' 被置于合法但未指定的状态
 
    std::cout << "a: " << *std::any_cast<std::string>(&a) //< 指针
        << "b: " << b << '\n';
}

其实从上面的代码看,c++的标准更倾向于再封装一层,保证安全性和易用性,但是这两个本身就是不好整合在一起的,所以只好看大佬儿表演吧。

四、总结

学习曲线由难到易容易为人接受,反之则很难被认可。c++能不能做好这个工作,把它的学习曲线平缓下来,是c++有没有未来的一个重要的标准。但是,做什么都一个度,过于简单,封装过度,它还是c++么?拭目以待。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值