如何结合可变参数模板(variadic templates)使用dynamic_pointer_cast实现多类型智能指针转换处理函数?
实现支持可变参数类型转换的
foo函数方案 我想要实现一个名为foo的函数,它能接受任意数量的类型参数(...TArgs),尝试把vector中的shared_ptr<Base>实例转换成对应的shared_ptr<TArgs...>类型。转换成功就执行输出操作,失败则继续处理后续逻辑。我尝试用dynamic_pointer_cast结合可变参数模板来实现,但对可变参数模板的用法不太熟悉,试了好几种代码变体都无法正常运行,请问该怎么正确编写这个函数?
原示例代码(存在语法错误):
// 示例程序 #include <iostream> #include <vector> #include <memory> template <typename T> struct Base { Base() = default; virtual ~Base() = default; }; struct Int : Base{ Int(int data) : _data(data){} int _data; }; struct Double : Base{ Double(double data) : _data(data){} double _data; }; struct Char : Base{ Char(char data) : _data(data){} char _data; }; template <typename ...TArgs> void foo(const std::vector<std::shared_ptr<Base>>& ptrVec) { for (const auto& ptr : ptrVec) { if (const auto x = std::dynamic_pointer_cast<TArgs>(ptr)...) // 语法错误 { std::cout << "casted with value = " << x->_data << "\n"; } } } int main() { const std::shared_ptr<Base> myInt = std::make_shared<Int>(1); const std::shared_ptr<Base> myDouble = std::make_shared<Double>(3.14); const std::shared_ptr<Base> myChar = std::make_shared<Char>('a'); foo<Int>({myInt}); std::cout << "\n"; foo<Double, Char>({myInt, myChar}); std::cout << "\n"; foo<Int, Double, Char>({myInt, myChar, myDouble}); return 0; }
问题分析
原代码有两个核心问题:
Base是模板类,但子类继承时未指定模板参数,这会导致编译错误(我们只需要Base作为多态基类,不需要模板参数);if (const auto x = std::dynamic_pointer_cast<TArgs>(ptr)...)的语法错误——可变参数模板不能直接用这种方式展开,需要用折叠表达式或递归模板来逐个处理类型参数。
解决方案1:每个元素尝试转换所有TArgs类型
如果你的需求是:对ptrVec中的每个shared_ptr<Base>,尝试转换成任意一个TArgs类型,只要转换成功就输出对应的值,推荐用C++17的折叠表达式实现:
#include <iostream> #include <vector> #include <memory> // 修正为非模板的虚基类,确保多态转换正常工作 struct Base { Base() = default; virtual ~Base() = default; }; struct Int : Base { Int(int data) : _data(data){} int _data; }; struct Double : Base { Double(double data) : _data(data){} double _data; }; struct Char : Base { Char(char data) : _data(data){} char _data; }; // 辅助函数:尝试将单个ptr转换成指定类型,成功则输出 template <typename T> void try_cast_single(const std::shared_ptr<Base>& ptr) { if (auto casted_ptr = std::dynamic_pointer_cast<T>(ptr)) { std::cout << "casted with value = " << casted_ptr->_data << "\n"; } } // 可变参数版本:展开所有TArgs类型,逐个尝试转换当前ptr template <typename... TArgs> void try_cast_all(const std::shared_ptr<Base>& ptr) { // 折叠表达式:依次调用每个类型的转换逻辑 (try_cast_single<TArgs>(ptr), ...); } template <typename ...TArgs> void foo(const std::vector<std::shared_ptr<Base>>& ptrVec) { for (const auto& ptr : ptrVec) { try_cast_all<TArgs...>(ptr); } } int main() { const std::shared_ptr<Base> myInt = std::make_shared<Int>(1); const std::shared_ptr<Base> myDouble = std::make_shared<Double>(3.14); const std::shared_ptr<Base> myChar = std::make_shared<Char>('a'); foo<Int>({myInt}); std::cout << "\n"; foo<Double, Char>({myInt, myChar}); std::cout << "\n"; foo<Int, Double, Char>({myInt, myChar, myDouble}); return 0; }
运行输出(与你的预期一致):
casted with value = 1 casted with value = a casted with value = 1 casted with value = a casted with value = 3.14
解决方案2:元素与TArgs类型一一对应转换
如果你的需求是:ptrVec中的第N个元素,尝试转换成TArgs中的第N个类型(一一对应),可以用索引序列实现:
#include <iostream> #include <vector> #include <memory> #include <utility> // 用于std::index_sequence struct Base { Base() = default; virtual ~Base() = default; }; struct Int : Base { Int(int data) : _data(data){} int _data; }; struct Double : Base { Double(double data) : _data(data){} double _data; }; struct Char : Base { Char(char data) : _data(data){} char _data; }; // 辅助实现函数:通过索引序列关联元素位置与类型 template <typename... TArgs, size_t... Is> void foo_impl(const std::vector<std::shared_ptr<Base>>& ptrVec, std::index_sequence<Is...>) { ( [&](){ // 防止索引越界(如果ptrVec元素数量少于TArgs数量) if (Is >= ptrVec.size()) return; const auto& ptr = ptrVec[Is]; if (auto casted_ptr = std::dynamic_pointer_cast<TArgs>(ptr)) { std::cout << "casted with value = " << casted_ptr->_data << "\n"; } }(), ... ); } template <typename ...TArgs> void foo(const std::vector<std::shared_ptr<Base>>& ptrVec) { // 生成与TArgs数量匹配的索引序列 foo_impl<TArgs...>(ptrVec, std::index_sequence_for<TArgs...>()); } int main() { const std::shared_ptr<Base> myInt = std::make_shared<Int>(1); const std::shared_ptr<Base> myDouble = std::make_shared<Double>(3.14); const std::shared_ptr<Base> myChar = std::make_shared<Char>('a'); foo<Int>({myInt}); std::cout << "\n"; // 元素顺序与TArgs类型一一对应 foo<Double, Char>({myDouble, myChar}); std::cout << "\n"; foo<Int, Double, Char>({myInt, myDouble, myChar}); return 0; }
运行输出:
casted with value = 1 casted with value = 3.14 casted with value = a casted with value = 1 casted with value = 3.14 casted with value = a
注意事项
- 确保编译器支持C17或更高版本(折叠表达式和
std::index_sequence是C17特性); dynamic_pointer_cast仅适用于多态类型,基类必须包含虚函数(这里通过虚析构函数满足要求)。
内容的提问来源于stack exchange,提问作者Gábor Pálovics
相关产品推荐
相关产品推荐

