编译时检查是否存在使用c++ 20的函数的模板专门化需要表达式



我正在开发一种事件系统,其中事件侦听器是在编译时确定的。为了实现这一点,我需要一个函数来告诉我参数class T是否实现了OnEvent()函数的特定专门化。

我目前的尝试使用c++ 20requires表达式:

template<class T>
class ScriptComponent
{
static_assert(std::is_base_of_v<Script, T>, "T must derive from Script!");
private:
T m_ScriptInstance;
public:
// Should return true if t.OnEvent<E>(e) can compile, false otherwise.
template<typename E>
constexpr static bool ImplementsEventFunction()
{
constexpr bool isImplemented = requires(const T& t, const E& e)
{
t.OnEvent<E>(e);
};
return isImplemented;
}
};

一个可以监听事件的类的例子:

class SomeScript : public Script
{
public:
// delete any non-specialized templates
template<typename E>
void OnEvent(const E&) = delete;
// the template specialization I want to check for
template<>
void OnEvent<SomeEvent>(const SomeEvent& e)
{
// do stuff
}
};

ImplementsEventFunction()的使用:

// should return true (there is a OnEvent() specialization for SomeEvent)
constexpr bool a = ScriptComponent<SomeScript>ImplementsEventFunction<SomeEvent>();
// should return false (there is no OnEvent() specialization for SomeOtherEvent)
constexpr bool b = ScriptComponent<SomeScript>ImplementsEventFunction<SomeOtherEvent>();

无论模板形参是什么,ImplementsEventFunction()总是返回false。很明显,我似乎用错了要求的表达,但我找不到我的错误。

没有理由要求OnEvent作为模板实现。这感觉像是对用户代码的过度控制。概念不是用来告诉用户如何准确地实现某些东西的。你将以某种方式调用接口,用户应该实现他们的代码,这样调用语法是有效的。

你的代码应该使用t.OnEvent(e)。您的代码没有理由需要在调用站点显式地指定模板参数。用户应该能够为某些特定的E实现OnEvent,作为一个非模板函数(或多个非模板重载),或者作为一个模板函数,使用模板实参推导来处理模板形参。

因此,正确的概念应该是:
template<typename T, typename E>
concept has_event_for = requires(T t, E e)
{
t.OnEvent(e);
};
class SomeScript : public Script
{
public:
void OnEvent(const SomeEvent& e)
{
// do stuff
}
//No need to delete anything or use templates.
};

不要像对待基类那样对待概念,在基类中,你显式地拼写出派生类接口必须严格遵循的确切参数。


如果您必须这样做,则必须在调用站点使用template关键字:

template<typename T, typename E>
concept has_event_for = requires(T t, E e)
{
t.template OnEvent<E>(e);
};

这又是一个不能这样做的理由。

最新更新