【问题标题】:Calling a templatized std::function stored in a map调用存储在地图中的模板化 std::function
【发布时间】:2017-01-06 13:24:03
【问题描述】:

我正在尝试创建一个基类来存储信号(以int 的形式)并允许我稍后使用它们来调用派生类指定的函数。它们存储在unordered_map 中,因为这正是我所需要的——1 个信号 = 1 个函数(而且速度很快)。

不过,我在给他们打电话时遇到了一些麻烦,收到了以下形式的巨大错误:

对‘(std::unordered_map<int, std::function<void()>, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<const int, std::function<void()> > > >::mapped_type {aka std::function<void()>}) (int&, int&)’的调用不匹配

编译错误来自signals_[_sig](args...);

const int SIGCHANGE = 1;

template<typename... Args>
class Signal {
private:
  std::unordered_map< int, std::function<void(Args...)> > signals_;

  inline void SetSignal(const int& _sig, std::function<void(Args...)> _func) {
    signals_[_sig] = _func;
  }

public:
  Signal() {}
  ~Signal() {}

  template<typename... Ts>
  void Sig(const int& _sig, Ts... args) {
    if (signals_.find(_sig) != signals_.end()) {
      signals_[_sig](args...);
    }
  }
};

class Obj : public Signal<> { 
private:
  int num_;

public:
  Obj* child;

  void Change(int _num) {
    num_ = _num;
    child->Sig(SIGCHANGE, _num);
  }

  void HandleChanged(int _num) {
    num_ = _num;
  }

};

int main() {
  Obj* obj1 = new Obj();
  Obj* obj2 = new Obj();

  obj1->child = obj2;

  obj1->Change(10);
}

另外,我不知道我是否正确使用了模板 - 自从我上次使用它们以来已经很久了。每个函数都应该有自己的模板吗?如果我想将模板用于成员变量,为什么必须在类之前指定模板?

【问题讨论】:

  • 好吧,显然要调用的函数(至少在模板中)是输入void func()(不带任何参数),你用两个int参数调用Sig,当然不能工作。特别是,使用template&lt;typename... Ts&gt; 是没用的,你只能拥有与整个Signal 模板相同的参数,因此该函数可以(并且应该)是void Sig(const int&amp; _sig, Args... args),与任何其他参数集一样无论如何它都不起作用.
  • 请发布 MCVE。您的代码脱离了上下文,让我们猜测如何重现编译错误。
  • @ChristianHackl 对不起,忘了电话是在别的地方,我会加进去的。
  • @areuz:如果您想对未来的读者更加友善,请添加main 函数和缺少的#includes,并删除明显不必要的内容,例如构造函数、析构函数和常量 :)
  • @ChristianHackl 是的,当然 :)

标签: c++ linux stl variadic-templates c++17


【解决方案1】:

由于你的函数有一个int 参数,你需要有

class Obj : public Signal<int>

而不是

class Obj : public Signal<>

后者使用原型为 void() 的函数创建映射,而您需要原型为 void(int) 的函数。

此外,模板化 Sig 函数是没有用的,因为无论如何您都无法使用除 Args 之外的任何其他参数集来调用它。所以它可以简单地是

  void Sig(const int& _sig, Args... args) {
    if (signals_.find(_sig) != signals_.end()) {
      signals_[_sig](args...);
    }
  }

如果您的目标是支持存储具有任意原型的函数,那确实需要以不同的方式完成,基本上是通过运行时多态性(映射中的所有项目都需要属于同一类型,因此类型本身需要在底层做调用多态性)。


编辑

根据信号类型定义函数原型的一种可能性。要求信号整数始终作为编译时常量传递 - 特定信号类型的不同函子存储在 std::tuple

const int SIGZERO = 0;
const int SIGCHANGE = 1;
const int SIGOTHER = 2;

// non-specialized template intentionally left empty
// (or can provide default prototype)
template<int SIG>
struct SignalPrototype;

template<>
struct SignalPrototype<SIGZERO>
{
    typedef std::function< void() > type;
};

template<>
struct SignalPrototype<SIGCHANGE>
{
    typedef std::function< void(int) > type;
};

template<>
struct SignalPrototype<SIGOTHER>
{
    typedef std::function< void(int, int) > type;
};


class Signal {
private:
    std::tuple<
        SignalPrototype<SIGZERO>::type,
        SignalPrototype<SIGCHANGE>::type,
        SignalPrototype<SIGOTHER>::type
    > signals_;

protected:

    template<int SIG>
    inline void SetSignal(typename SignalPrototype<SIG>::type _func) {
        std::get<SIG>(signals_) = _func;
    }

public:

    template<int SIG, typename... Ts>
    void Sig(Ts... args) {
        SignalPrototype<SIG>::type func = std::get<SIG>(signals_);
        if (func)
            func(args...);
    }

};

struct MyHandler : SignalPrototype<SIGCHANGE>::type
{
    void operator()(int x)
    {
        std::cout << "Called MyHandler with x = " << x << std::endl;
    }
};


class Obj : public Signal {
private:
    int num_;

public:
    Obj* child;

    Obj()
    {
        SetSignal<SIGCHANGE>(MyHandler());
    }

    void Change(int _num) {
        num_ = _num;
        child->Sig<SIGZERO>();
        child->Sig<SIGCHANGE>(_num);
        child->Sig<SIGOTHER>(1, 2);
    }

    void HandleChanged(int _num) {
        num_ = _num;
    }

};

(一般来说,Signal 成为 Obj 的成员而不是继承实际上可能会更好)

【讨论】:

  • 嗯。我懂了。我错误地使用了模板。我想要实现的是不同功能模板的同质容器。
  • 哎呀,没有看到你的编辑,是的,这就是我想要实现的......那么模板不可能吗?嗯...可以使用 Lambdas 吗?
  • @areuz:您的Signal 模板是一种创建许多不同容器的机制,例如Signal&lt;int&gt;Signal&lt;double, float&gt;Signal&lt;std::string, int, int&gt;。它不会导致 一个 同质容器。可变参数模板仍然是一种编译时机制。
  • @ChristianHackl 是的,但是我应该使用什么(谷歌搜索“运行时多态性”并没有真正产生多少)?我想我记得很久以前我用 lambdas 做了这个,但它很丑。
  • 嗯,多态存储很棘手,一方面是如何存储它(更容易),另一方面是如何正确调用它(棘手的部分)。如果我能够弄清楚如何做到这一点,我正在尝试进行测试,但正如所说的那样,这很棘手:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-13
  • 2013-01-18
  • 2020-05-06
  • 1970-01-01
  • 2021-10-30
相关资源
最近更新 更多