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

继承T并使用using T::T时,模板类中如何初始化const成员?

解决思路与实现方案

嘿,这问题我太懂了!咱们要解决的核心就是把开销大的计算型const成员函数换成const成员变量,同时还要保住原来的继承结构——毕竟大量函数都依赖B1/B2的指针/引用,可不能改这个关系。

核心问题分析

原来的is_early_bird()/is_hunting()是每次调用都重复计算的const函数,开销拉满;但如果换成const成员变量,我们得在对象构造时就完成初始化。不过之前用using T::T继承了基类构造函数,这些继承来的构造函数没法帮我们初始化派生类自己的const成员,所以得自定义构造函数来搞定这件事。

具体实现步骤

  1. 替换函数为const成员变量:把原来的成员函数改成const bool类型的成员变量,比如bird类里的const bool is_early_bird_;。
  2. 用可变参数模板构造函数转发参数:因为B1和B2的构造函数参数不一样,我们需要写一个泛型构造函数,完美转发所有参数给基类T的构造函数,同时在初始化列表里计算并初始化const成员变量。
    • 这里要注意:基类会先于派生类成员初始化,所以构造时this->wake_up_time_已经是有效的,可以安全用来计算。

修改后的完整代码

#include <iostream>
#include <ctime>
#include <string>
#include <memory>
#include <utility> // 用于std::forward

class A {
protected:
    int wake_up_time_;
public:
    A(int wake_up_time): wake_up_time_(wake_up_time) { }
    virtual void hello() const {
        std::cout << "I wake up at " << wake_up_time_;
    }
};

class B1 : public A {
public:
    std::string b;
    B1(int a, std::string b): A(a), b(std::move(b)) { } // 加std::move优化拷贝
};

class B2 : public A {
public:
    int c;
    double d;
    B2(int a, int c, double d): A(a), c(c), d(d) { }
};

template<class T>
class bird : public T {
    const bool is_early_bird_; // 替换成const成员变量
public:
    // 可变参数模板构造函数,完美转发参数给基类T,同时初始化成员变量
    template<typename... Args>
    bird(Args&&... args) 
        : T(std::forward<Args>(args)...), 
          is_early_bird_(this->wake_up_time_ < 6) {}

    void hello() const override {
        std::cout << (is_early_bird_ ? "I am an early bird!" : "Getting up is hard...") << std::endl;
    }
};

template<class T>
class cat : public T {
    const bool is_hunting_; // 替换成const成员变量
public:
    template<typename... Args>
    cat(Args&&... args) 
        : T(std::forward<Args>(args)...), 
          is_hunting_(this->wake_up_time_ < 5 || this->wake_up_time_ > 22) {}

    void hello() const override {
        std::cout << (is_hunting_ ? "Time to kill stuff" : "Time to sleep") << std::endl;
    }
};

int main() {
    std::unique_ptr<A> ptr;
    {
        ptr.reset(new bird<B1>(5, "..."));
        std::cout << "B1 has value " << dynamic_cast<B1*>(ptr.get())->b << std::endl;
    }
    ptr->hello();
    {
        ptr.reset(new cat<B1>(12, "xyz"));
        std::cout << "B1 has value " << dynamic_cast<B1*>(ptr.get())->b << std::endl;
    }
    ptr->hello();
    {
        ptr.reset(new cat<B2>(24, 3, 12.5));
        B2* l_ptr = dynamic_cast<B2*>(ptr.get());
        std::cout << "B2 has value " << l_ptr->c << " and " << l_ptr->d << std::endl;
    }
    ptr->hello();
    {
        ptr.reset(new B2(10, 7, 3.33));
        B2* l_ptr = dynamic_cast<B2*>(ptr.get());
        std::cout << "B2 has value " << l_ptr->c << " and " << l_ptr->d << std::endl;
    }
    ptr->hello();
    return 0;
}

关键细节解释

  • 完美转发:用std::forward<Args>(args)...可以保证参数的类型(左值/右值)被正确传递给基类构造函数,避免不必要的拷贝,提升效率。
  • 初始化顺序:C++中基类的构造总是先于派生类成员的初始化,所以在初始化is_early_bird_时,this->wake_up_time_已经由基类A(通过T的构造函数)初始化完成,完全可以安全使用。
  • 兼容性保持:bird<T>和cat<T>依然是T的派生类,所以dynamic_cast<B1*>或dynamic_cast<B2*>依然能正常工作,完全兼容原来那些接收B1/B2指针/引用的函数。

运行结果

修改后的代码运行结果和原代码完全一致:

B1 has value ...
I am an early bird!
B1 has value xyz
Time to sleep
B2 has value 3 and 12.5
Time to kill stuff
B2 has value 7 and 3.33
I wake up at 10

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:09:05