前序文章请看:
C++模板元编程详细教程(之一)
C++模板元编程详细教程(之二)
C++模板元编程详细教程(之三)
C++模板元编程详细教程(之四)
C++模板元编程详细教程(之五)
C++模板元编程详细教程(之六)
C++模板元编程详细教程(之七)
实现一个动态的get
如果读者对元组工具,也就是std::tuple很熟悉的话,那么对std::get也一定不陌生。我们可以通过std::get<N>(tu)的方式把原组的第N个元素取出来。
但是,不知道大家有没有思考过这样一个问题,std::get是静态模板工具,所以参数N必须要编译期确定。那为什么STL不提供一个动态序号获取元组元素的能力呢?也就是说类似tu.get(n),其中n是函数参数(运行期数据)。最主要的原因就在于类型不确定,假如说我们真的要给tuple提供一个get方法,那么方法的返回值是不确定的,于是这就成了一个RTTI(Run-Time Type Identification)方法。而STL是模板库,并不提供任何RTTI行为,自然也就不会提供类似的方法。
但假如,我们在实际开发中,针对某个元组,可以确定(或可以正确处理)它的类型,在这种情况下希望可以提供一个动态的get工具应当如何来做?举例来说:
class Base {
public:
virtual void f() const = 0;
};
class Ch1 : public Base {
public:
void f() const override {
}
};
class Ch2 : public Base {
public:
void f() const override {
}
};
// 利用多态调用f方法
template <typename... Args>
std::enable_if_t<
std::conjunction_v<
std::is_base_of<Base, Args>...
>, void>
InvokeF(const std::tuple<Args...> &tup, size_t index) {
// 考虑这里应该怎么写
}
void Demo() {
// 一个元组,里面都是Base的子类对象
std::tuple tu{
Ch1{
}, Ch2{
}, Ch1{
}};
InvokeF(tu, 1); // 调用Ch2的f方法,注意这个参数1是运行期数据
}
解释一下上面例程,Ch1和Ch2都是Base类的子类,InvokeF函数是传入一个元组tup和一个序号index,我们要把tup的第index个元素拿出来,去调用它的f方法,这里要求tup的元素都必须是Base的子类。
现在问题就在于,index是一个运行期数据,我们用std::get<index>肯定是不可以的,那怎么办?只能采用编译期展开的方式。
具体来说就是,由于我们这里的元组类型是编译期确定的,那么对应的变参数量也就是确定的(比如上面例程中就是3个),那么,我们就在编译期生成分别应对假如index就是这种情况,应当怎么去做。
以上面例程为例,由于编译期不知道index可能是几,但针对std::tuple<Ch1, Ch2, Ch1>这种类型的元组来说,index只能是0、1、2这3种情况时才可以有合法处理。所以,我们就分别写出当index为0、1、2时,应当做的处理。这个就叫做「编译期展开」,也就是在编译期枚举出运行时可能传入的所有数据,分别生成对应的代码指令,然后当运行期数据确定的时候去执行对应的指令。
如果手动来写,就应该写成:
template <typename... Args>
void InvokeF_0(const std::tuple<Args...> &tu) {
std::get<0>(tu).f();
}
template <typename... Args>
void InvokeF_1(const std::tuple<Args...> &tu) {
std::get<1>

文章探讨了C++模板元编程中如何在运行期通过动态索引获取元组元素,分析了std::get的静态特性以及为何STL不提供动态get。通过示例展示了如何利用模板元编程的编译期展开技术,实现在已知元组类型的情况下,根据运行期数据调用元素的方法。此外,还介绍了如何生成序列并应用到元组展开,以实现用元组调用函数的功能。
&spm=1001.2101.3001.5002&articleId=128933496&d=1&t=3&u=976900e1e86f4655846ed81c245196dc)
1050

被折叠的 条评论
为什么被折叠?



