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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM