无法将 std::function 移动到地图中

Cannot move std::function into map

我有一些 Signal 原型 class

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

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

接下来是模板单例 SignalCollection class,它会自动为 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)));
    }
};

最后我有一个函数可以推导某些 Signal

SignalCollection 类型
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
}

T&&如果T不推导就不是转发引用,反之

template<class T>
auto& get_collection_for_signal(T&& t)

T 在您的示例中被推断为对 Signal<void()> 的引用:

auto& t = get_collection_for_signal(sgl);

所以它 returns:

return SignalCollection<T>::Instance();

即:

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

这是胡说八道。

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

明智的设计,你有无用的类型 -- Signal<T> 写的是一个 std 函数,带有什么都不做的包袱 -- 全局单例,类型推导创建全局状态。

我明白你想做什么,但这就像想用稻草做一辆消防车一样。

这里的问题是

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

当你这样称呼它时

auto& t = get_collection_for_signal(sgl);

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

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

删除了 cv 资格和 T 的引用(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 是 class 类型的一部分。您需要使其成为自己的模板才能具有转发参考。您可以将其更改为

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

最后

void main()

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

其他答案都很好地解释了错误,但我会推荐其他解决方案来解决问题。

我看到在您的代码中您实际上不需要转发引用。您仅将其用于扣除。在这种情况下,您可以通过 T const& 来进行论证,它总是会在没有引用的情况下推导出 T

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