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

使用模板实现树形结构遍历的访问者模式报错问题

问题原因

C++标准明确禁止虚函数为函数模板。这是因为虚函数的调用依赖编译阶段生成的虚函数表(vtable),而模板函数仅在实例化时才生成具体的函数实现。编译器无法提前预知所有可能的模板参数T,也就无法将所有对应的accept模板实例预载入虚表,因此代码中Expr::accept的写法违反了语言规则,触发编译错误。

解决方案

我们可以调整设计,移除虚模板函数,改用动态类型转换+模板方法的方式实现多态调用,同时保留返回任意类型的能力。以下是修改后的完整代码:

#include <iostream>
#include <stdexcept>

#define print(x) std::cout << x << "\n"; 

class Add;
class Number;

// 模板化的访问者基类
template <class T>
class Visitor {
public:
    virtual T visitAdd(Add* add) = 0;
    virtual T visitNumber(Number* num) = 0;
};

// 加法访问者实现
template <class T>
class Adder : public Visitor<T> {
public:
    T visitAdd(Add* add) override;
    T visitNumber(Number* num) override;
};

// 表达式基类:移除虚模板函数,改用模板方法+动态转换
class Expr {
public:
    template <class T>
    T accept(Visitor<T>* visitor) {
        if (auto* add_node = dynamic_cast<Add*>(this)) {
            return add_node->accept(visitor);
        }
        if (auto* num_node = dynamic_cast<Number*>(this)) {
            return num_node->accept(visitor);
        }
        throw std::invalid_argument("Unknown expression type");
    }

    virtual ~Expr() = default; // 增加虚析构,避免内存泄漏
};

class Add : public Expr {
public:
    Expr* left;
    Expr* right;

    Add(Expr* left, Expr* right) : left(left), right(right) {}

    template <class T>
    T accept(Visitor<T>* visitor) {
        return visitor->visitAdd(this);
    }

    ~Add() override {
        delete left;
        delete right;
    }
};

class Number : public Expr {
public:
    int value;

    Number(int value) : value(value) {}

    template <class T>
    T accept(Visitor<T>* visitor) {
        return visitor->visitNumber(this);
    }
};

template <class T>
T Adder<T>::visitAdd(Add* add) {
    return add->left->accept(this) + add->right->accept(this);
}

template <class T>
T Adder<T>::visitNumber(Number* num) {
    return static_cast<T>(num->value); // 显式转换,确保类型兼容
}

int main() {
    // 表达式:(5 + 2) + (7 + (4 + 2))
    Expr* expression = new Add(
        new Add(new Number(5), new Number(2)),
        new Add(new Number(7), new Add(new Number(4), new Number(2)))
    );

    // 计算整数和
    Adder<int> intAdder;
    print(expression->accept(&intAdder));

    // 计算浮点数和
    Adder<float> floatAdder;
    print(expression->accept(&floatAdder));

    delete expression; // 释放内存
    return 0;
}
修改说明
  1. 移除虚模板函数:删除Expr中virtual template形式的accept声明,改用非虚模板方法,通过dynamic_cast判断实际节点类型,调用对应子类的accept模板函数。
  2. 增加虚析构函数:在Expr基类中添加虚析构,确保子类对象能被正确销毁,避免内存泄漏。
  3. 显式类型转换:在Adder<T>::visitNumber中添加static_cast<T>,确保整数value能安全转换为目标类型(如float)。
  4. 内存管理:在Add类的析构函数中递归删除子节点,main中最终删除根节点,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Jacob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 08:39:52