C++学习笔记:variant及其overload模式
本文最后更新于:2023年3月15日 中午
1 std::variant
std::variant
是C++17引入的一个替代union
的容器。
一个std::variant
的实例包含它的某一个类型的值,这个值不能是引用、C数组、void。std::variant
默认初始化为其中的第一个类型,因此,第一个类型必须有一个默认的构造函数。下面是一个来自std::variant - cppreference.com的例子:
1 |
|
2 std::visit
std::visit
允许你对于容器中的 std::variant
使用 visitor
模式。visitor
必须是可以调用的。一般可以调用的都是函数,函数对象或者 lambda
表达式。在下面的例子中使用lambda
表达式:
1 |
|
运行结果如下:
在上述代码中使用了泛型lambda(generic lambda
)表达式,但是我们想针对每一个数据类型使用不同的 lambda 表达式,这需要用到 overload 模式。
3 overload
模式
使用 overload 模式,可以访问单独的 lambda 表达式。
1 |
|
程序输出结果如下:
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
:这是一个类模板的声明。template<class... Ts>
:overloaded类的模板参数为可变长的参数包 Ts。假设 Ts 包含T1,T2,...,TN
,那么这一句声明可以展开为:template<class T1, class T2, ... , class TN>
。struct overloaded : Ts...
:overloaded 类的基类为参数包 Ts 内所有的参数类型。假设 Ts 包含 T1, T2, … , TN,那么这一句声明可以展开为:struct overloaded : T1, T2, …, TN。using Ts::operator()...;
:这是一个变长 using 声明。假设 Ts 包含 T1, T2, … , TN,那么这一句声明可以展开为:using T1::operator(), T2::operator(), ..., TN::operator();
也就是说,overloaded 类的基类即参数包 Ts 内所有的参数类型的函数调用操作符均被 overloaded 类引入了自己的作用域。
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
:这是一个自动推断向导,用于帮助编译器根据overloaded 构造器参数的类型来推导 overloaded 的模板参数类型。这个自动推断向导告诉编译器,如果 overloaded 构造器所有参数的类型的集合为 Ts,那么 overloaded 的模板参数类型就是 Ts 所包含的所有类型。
也就是说如果表达式
a1, a2, …, an
的类型分别为T1, T2, …, TN
,
那么构造器表达式overloaded {a1, a2, ..., an}
的类型就是overloaded<T1, T2, ..., TN>
。*overloaded s{
[](int){cout << "int" << endl;},
[](double){cout << "double" << endl;},
[](string){cout << "string" << endl;},
};
overloaded 类的实例 s 的构造器包含 3 个 lambda 参数,也可以看作 3 个各自包含一个 operator() 的函数对象。根据 overloaded 类的定义,s 对象将继承这 3 个 lambda(函数对象)的 operator() ,也就是说这3个lambda的 operator() 即函数体在 s 对象内部形成重载关系。
根据 overloaded 类的自动推断向导,s 对象的类型为
overloaded<T1, T2, T3>
,其中 T1, T2, T3为3个lambda参数的类型。
使用overload模式访问std::variant
:
1 |
|
输出结果如下:
如果没有overload,则必须为调用operator()
写一个具有三个重载的单独的类或结构:
1 |
|
参考文章:
Visiting a std::variant with the Overload Pattern - ModernesCpp.com