我有一些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
}
答案 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();
}