无法将std :: function移入地图

时间:2019-03-13 18:17:38

标签: c++ templates move-semantics

我有一些Signal原型类

#include <map>
#include <string>
#include <functional>

template <class T>
class Signal
{
public:
    std::function<T> slot;
};

接下来是模板单例SignalCollection类,该类自动为Signal生成适当的类型

template <class T>
class SignalCollection
{
private:
    SignalCollection() {}
    SignalCollection(const SignalCollection&) = delete;
    SignalCollection& operator= (const SignalCollection&) = delete;
public:

    static SignalCollection& Instance()
    {
        static SignalCollection br{};
        return br;
    }

    std::map<std::string, T> signals_map;

    void add(T&& signal)
    {
        this->signals_map.insert(std::make_pair("a", std::forward<T>(signal)));
    }
};

最后我有一个函数可以为某些SignalCollection推导Signal类型

template<class T>
auto& get_collection_for_signal(T&& t)
{
    return SignalCollection<T>::Instance();
}

问题是,我无法将值添加到集合图。这是主要的:

void foo()
{

}
void main()
{
    Signal<void()> sgl = Signal<void()>{}; //Create signal
    sgl.slot = foo;                       //add slot

    auto& t = get_collection_for_signal(sgl); //Get collection for this signal which is SignalCollection<Signal<void()>>

    t.add(sgl); //error 1
    t.signals_map["a"] = sgl; //error 2
}

3 个答案:

答案 0 :(得分:6)

这里的问题是

template<class T>
auto& get_collection_for_signal(T&& t)
{
    return SignalCollection<T>::Instance();
}

当您称呼它

auto& t = get_collection_for_signal(sgl);

T推导为Signal<void()>&,这意味着您返回的SignalCollection<Signal<void()>&>不是您想要的。您需要做的是从类型中删除引用。您可以使用

template<class T>
auto& get_collection_for_signal(T&&)
{
    return SignalCollection<std::remove_cv_t<std::remove_reference_t<T>>>::Instance();
}

T中删除了 cv 资格和引用(C ++ 20为我们提供了std::remove_cvref,因此可以使用单个帮助程序完成)。

您也可以通过以下方式获得相同的行为

template<class T>
auto& get_collection_for_signal(T)
{
    return SignalCollection<T>::Instance();
}

这涉及到很多打字工作,并且由于顶级简历资格被剥夺了,而且永远不会推论出参考,因此您可以得到相同的行为。


您的add函数也有问题。

void add(T&& signal)
{
    this->signals_map.insert(std::make_pair("a", std::forward<T>(signal)));
}

不使用转发引用,因为T是类类型的一部分。您需要使其具有自己的模板才能具有转发参考。您可以将其更改为

template<typename U>
void add(U&& signal)
{
    this->signals_map.insert(std::make_pair("a", std::forward<U>(signal)));
}

最后

void main()

总是错误main()被授权返回int。通读What should main() return in C and C++?了解更多信息。

答案 1 :(得分:2)

T&&不是转发参考,如果不推论T则相反。

template<class T>
auto& get_collection_for_signal(T&& t)
在您的示例使用中,推导了

T作为对Signal<void()>的引用:

auto& t = get_collection_for_signal(sgl);

因此它返回:

return SignalCollection<T>::Instance();

这是:

SignalCollection<Signal<void()>&>::Instance()

这是胡说八道。

刷新转发引用和l / rvalue引用。在代码中每次使用它都是错误的。

明智的设计是,您有无用的类型-Signal<T>是带有行李的std函数,它什么也不做-全局单例,并通过类型推导创建全局状态。

我明白了您想做的事情,但这就像想用稻草做成消防车一样。

答案 2 :(得分:2)

其他答案很好地解释了该错误,但我建议使用另一种解决方案来解决该问题。

我看到在您的代码中,您实际上不需要转发参考。您仅将其用于扣除。在这种情况下,您可以通过T const&来接受您的论点,这总是会得出没有参考的T

template<class T>
auto& get_collection_for_signal(T const& t)
{
    return SignalCollection<T>::Instance();
}