C++ 之 Python 字典类型实现

变体类型 std::variant

在此之前需要先介绍一下类模板std::variant,其表示一个类型安全的联合体。 std::variant 的一个实例在任意时刻要么保有其一个可选类型之一的值,要么在错误情况下无值。与union在聚合初始化中的行为一致, 若 variant 保有某个对象类型 T 的值,则直接于 variant 的对象表示中分配 T 的对象表示。不允许 variant 分配额外的(动态)内存。简单来说就是一个可变类型的变量,其获取方法跟元组的第一个获取方法类似。代码如下

#include <variant>
#include <string>
#include <cassert>
 
int main()
{
    std::variant<int, float> v, w;
    v = 12; // v 含 int
    int i = std::get<int>(v);
    w = std::get<int>(v);
    w = std::get<0>(v); // 与前一行效果相同
}

更加优秀的状态是std::visitoverloaded模板函数相互配合实现,代码如下:

#include <variant>
#include <iostream>
#include <sstream>
#include <string>

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; 

int main() {
	std::stringstream convert;
	std::variant<int, long long int, std::string, double> value = 12.3;
	std::visit(overloaded{
	        [&convert](int value) { convert << value << 'i'; },
	        [&convert](long long int value) { convert << value << 'i'; },
	        [&convert](double value) { convert << value; },
	        [&convert](const std::string &value) { convert << '"' << value << '"'; },
	}, value);
	std::cout << convert.str();
}

如果看懂了,你肯定感觉很惊艳。不过作为一个入门级选手的我,属实看不懂。首先是overload就没看懂,不错是template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; 模板参数的自动推导,如果你知道这是类型推导的一步,那已经明白1/3了。

[&convert](int value) { convert << value << 'i'; },
[&convert](long long int value) { convert << value << 'i'; },
[&convert](double value) { convert << value; },
[&convert](const std::string &value) { convert << '"' << value << '"'; }

这四句话都是如果不明白Lambda 表达式可以查看博文C++ 11 新特性总结,而Lambda 表达式是一个函数对象,其作为overload参数输入,会使得编译器根据自动推导命令,直接将其类作为模板参数表实现类型的声明即获得一个类overloaded<Lambda1,Lambda2,Lambda3>

接下来是using的变长声明using 声明还挺长一段,详细内容可以查看C++ 中文指南,简单来说,就是直接将父类中的函数直接拿来用,效果就像不是继承而来,在类内自己实现的。这样就有一个效果,如果父类和子类具有同名函数,便形成了函数重载。所以这里的using Ts::operator()...是将所有根据Lambda表达式所获取的继承类Ts()运算符函数直接拿来用,并且由于函数名一样出现类似函数重载的效果。

最后是std::visit函数,其声明如下:

template <class Visitor, class... Variants>
constexpr /*...*/ visit(Visitor&& vis, Variants&&... vars);

其实现的功能是,将vars的可能值传入vis中。

所以最终便会实现,根据variant中可用参数类型不同,最终生成不一样的字符串,传入字符串流对象convert中。

到此字典类型核心便的实现了一般,下一步是字符串映射,使用C++标准库中的std::mapstd::unordered_map 便可以实现了。下面是示例代码,可见可以轻松实现Python的字典效果。

#include <variant>
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

int main() {

    std::variant<int, long int, char, std::string, float, double> value1;
    std::unordered_map<std::string, std::variant<int, long int, char, std::string, float, double>> dictionary;

    dictionary["a"] = "Hello";
    dictionary["b"] = 'I';
    dictionary["c"] = 2;
    dictionary["d"] = 3.14159f;

    std::stringstream convert;

    for (const auto &item : dictionary) {
        convert.clear();
        convert.str("");
        std::visit(overloaded{
                [&convert](char value) { convert << value; },
                [&convert](int value) { convert << value; },
                [&convert](long int value) { convert << value; },
                [&convert](float value) { convert << value; },
                [&convert](double value) { convert << value; },
                [&convert](const std::string &value) { convert << value; },
        }, item.second);
        std::cout << item.first << ' ' << convert.str() << '\n';
    }
}

打印信息如下:

d 3.14159
c 2
a Hello
b I
posted @ 2021-04-28 16:45  FlameAlpha  阅读(950)  评论(0编辑  收藏  举报