【问题标题】:C++ class template with inheritance具有继承的 C++ 类模板
【发布时间】:2021-08-27 09:57:14
【问题描述】:

我正在努力解决一个关于从模板中链接两个派生类的问题。假设我们有一个模板基类 Param_B,它将其子类(Param_S 或 Param_M)之一作为参数。 Param_B 类的模板参数进一步用于定义其他局部变量(_parent 和回调函数 _func)。对我来说,需要有 Param_S 或 Param_M 来动态调用函数。

问题是,在我创建了指向孩子的指针并向它们添加计时器和函数之后,我想要一个 _link 变量,它可以在两个孩子类型之间交换信息。例如。从 Param_S 我想通过使用 _link 变量来更改 Param_M 计时器的超时。我

#include <stdio.h>
#include <memory>
#include <vector>

class Param_S;
class Param_M;

template <class T>
class Functionn{
    Functionn(){};
    ~Functionn(){};
private:
    std::function<void(std::shared_ptr<T>)> _callback;
};


template <class T>
class Timer
{
public:
    Timer(){ timeout = 0; };
    ~Timer(){};

    void setTimeout(const int time){    timeout = time; };
    std::weak_ptr<Functionn<T>>& getFunction(){ return _func; };
    std::weak_ptr<T> getParent(){   return _parent;};

private:
    int timeout;
    std::weak_ptr<T> _parent;
    std::weak_ptr<Functionn<T>> _func;
};



template <class T>
class Param_B {
public:
    Param_B(){};
    ~Param_B(){};

    std::vector<std::shared_ptr<Timer<T>>>& getTimers(){
        return _timers;
    }
    void addTimer(std::shared_ptr<Timer<T>> _t){
        _timers.push_back(std::move(_t));
    }
    std::weak_ptr<Param_B> getLink(){
            return _link;
    }
    void setLink(std::weak_ptr<Param_B> link){
            _link = link;
    }

private:
    std::weak_ptr<Param_B> _link;
    std::vector<std::shared_ptr<Timer<T>>> _timers;
};



class Param_S : public Param_B<Param_S>
{
public:
    Param_S(){ paramS = -2; };
    ~Param_S(){};

    void setValue(){
        paramS = 33;
    }

    int getValue(){ return paramS;};

private:
    float paramS;
};


class Param_M : public Param_B<Param_M>
{
public:
    Param_M(){ parammM = -4; };
    ~Param_M(){};
    int getValue(){ return parammM; };

private:
    int parammM;

};


class sys{
public:

    std::vector<std::shared_ptr<Param_S>> getParams(){
        return _params;
    }

private:
    std::vector<std::shared_ptr<Param_S>> _params;
};

class module{
public:
    std::vector<std::shared_ptr<Param_M>> getParams(){
        return _params;
    }
private:
    std::vector<std::shared_ptr<Param_M>> _params;
};

void main(){

    std::shared_ptr<sys> _sys = std::make_shared<sys>();
    _sys->getParams().push_back(std::make_shared<Param_S>());
    _sys->getParams()[0]->addTimer(std::make_shared<Timer<Param_S>>());


    std::shared_ptr<module> _mod = std::make_shared<module>();
    _mod->getParams().push_back(std::make_shared<Param_M>());
    _mod->getParams()[0]->addTimer(std::make_shared<Timer<Param_M>>());


    _sys->getParams()[0]->setLink(_mod->getParams()[0]); // this not work , the conversion failed 
}

感谢您的帮助。

【问题讨论】:

  • 这两个孩子不是多态的。为此,您需要template covariance,它通常会产生比它解决的问题更多的问题。这不是您所面临的确切情况,但问题是相同的
  • getParams() 应该返回 sysmodule 中的引用
  • 你想用getLink()做什么?
  • @Caleth 正在调用getLink() 然后我可以获取链接参数(param_M 或 Param_S)的计时器并更改计时器超时。还有一点就是调用附在定时器上的函数。

标签: c++ class


【解决方案1】:

您需要为Param_B 提供非模板库。它不能涉及与模板参数相关的任何内容,但您可以在此处使用虚函数

class Param_B_Base {
public:
    virtual ~Param_B_Base() = default;

    std::weak_ptr<Param_B_Base> getLink(){
            return _link;
    }
    void setLink(std::weak_ptr<Param_B_Base> link){
            _link = link;
    }

    // an example of possible behaviour
    virtual void setTimeouts(const int time) = 0;

    // another example of possible behaviour
    virtual void invokeHandlers() = 0;

private:
    std::weak_ptr<Param_B_Base> _link;
};

template <class T>
class Functionn{
    void operator()(std::shared_ptr<T> ptr) { _callback(ptr); }
private:
    std::function<void(std::shared_ptr<T>)> _callback;
};

template <class T>
class Timer
{
public:

    void setTimeout(const int time){    timeout = time; };
    void invoke() { if (auto func = _func.lock()) { if (auto parent = parent.lock() { (*func)(parent); } } }

private:
    int timeout = 0;
    std::weak_ptr<T> _parent;
    std::weak_ptr<Functionn<T>> _func;
};

然后你可以有一个子类的模板

template <class T>
class Param_B : public Param_B_Base {
public:

    std::vector<std::shared_ptr<Timer<T>>>& getTimers(){
        return _timers;
    }
    void addTimer(std::shared_ptr<Timer<T>> _t){
        _timers.push_back(std::move(_t));
    }

    // The implementation of the virtual methods can know about T
    void setTimeouts(const int timeout) override {
        for (std::shared_ptr<Timer<T>> timer : _timers) {
            timer->setTimeout(timeout);
        }
    }

    void invokeHandlers() override {
        for (std::shared_ptr<Timer<T>> timer : _timers) {
            timer->invoke();
        }
    }
private:
    std::vector<std::shared_ptr<Timer<T>>> _timers;
};

【讨论】:

  • 这比设置函数链接更好。
【解决方案2】:

我建议在两个派生类之间建立与类型无关的链接(即,使链接成为 std::function 而不是指向其他类型实例的指针。

如果您想将其保留为指针,则问题在于您在 Param_B 模板类中使用了 Param_B

void setLink(std::weak_ptr&lt;Param_B&gt; link)

在上述行中,Param_BParam_B&lt;Param_M&gt; 的实例上调用该方法时将引用Param_B&lt;Param_M&gt;,在@987654328 的实例上调用该方法时它将引用Param_B&lt;Param_S&gt; @。

您需要做的是向Param_B 添加第二个模板参数,以告诉它链接的派生类型是什么。

template <class T, class L>
class Param_B {
public:
    Param_B(){};
    ~Param_B(){};

    std::vector<std::shared_ptr<Timer<T>>>& getTimers(){
        return _timers;
    }
    void addTimer(std::shared_ptr<Timer<T>> _t){
        _timers.push_back(std::move(_t));
    }
    std::weak_ptr<Param_B> getLink(){
            return _link;
    }
    void setLink(std::weak_ptr<Param_B<L, T>> link){
            _link = link;
    }

private:
    std::weak_ptr<Param_B<L, T>> _link;
    std::vector<std::shared_ptr<Timer<T>>> _timers;
};

然后你需要Param_M 的前向声明,这样它就可以成为Param_S 的模板参数。

class Param_M;

class Param_S : public Param_B<Param_S, Param_M>
{
...
};
class Param_M : public Param_B<Param_M, Param_S>
{
...
};

编辑:

如果您知道派生类型将如何交互,请不要将函数用作链接;请参阅@Caleth 的答案。

【讨论】:

  • 这假设链接总是指向一个精确的镜像类型,而不是Param_B的任何实例化
  • 另外,getParams 方法需要返回对向量的引用。
  • 感谢您提供此解决方案。它确实有效,但我的想法是有一个 _link 参数,它可以是 Param_M 或 Param_S 类型并且更灵活。
  • @Caleth 是的,你是对的。这就是为什么我的第一个建议是对链接使用函数。但是,根据预期用途,它可能需要将多个函数存储为链接(例如,多个成员的设置器)。
  • @durneata-dan 不幸的是,我认为没有办法使用指向Param_B 另一个实例化的指针的链接成员来做到这一点。没有“通用”Param_B 这样的东西。如果您可以回答 @Caleth 关于您打算如何使用该链接的问题,这将有助于用户提出其他设置链接的方法。
猜你喜欢
  • 2012-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-16
相关资源
最近更新 更多