You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合可变参数模板(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;
}

问题分析

原代码有两个核心问题:

  1. Base是模板类,但子类继承时未指定模板参数,这会导致编译错误(我们只需要Base作为多态基类,不需要模板参数);
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 16:24:07