从属名称不是类型,带有 SFINAE 的模板

dependent name is not a type, templates with SFINAE

我正在尝试注册这样的事件:

void myResize(unsigned int width, unsigned int height);

int main() {
    registerEvent(Event::Resize, myResize);
}

首先,我尝试为每个事件创建一个重载,但后来我意识到如果有两个具有相同参数的事件将无法工作,所以我尝试使用模板和 SFINAE。

经过几个小时的网上冲浪,我对它们有了最低限度的了解。

这是我最后的方法:

#include <type_traits>

enum class Event { Resize };

template<typename T>
struct enabler { typedef void type; };

template<Event, typename...>    
struct validate_event : std::false_type {};

template<Event Evt, typename T>
struct validate_event<Evt, T> : std::false_type {};

template<> //This is the only case when registerEvent should be validated
struct validate_event<Event::Resize, unsigned int, unsigned int> : std::true_type {};

template<Event Type, typename ...Args>
typename enabler<
    typename std::enable_if <
        validate_event<Type, Args...>::value // <-- Error Here
    > ::type
> ::type
registerEvent(Type type, void (*f)(Args...));

该方法会抛出几个语法错误(自 "Error Here" 标记线并贯穿整个堆栈跟踪),并发出警告,告知:

'validate_event<__formal,<unnamed-symbol>...>::value': dependent name is not a type.

在互联网上查找警告后,似乎是因为未将类型定义为 typename

也尝试使用 value 而不是 type,因为它们在 integral constants of type bool (As true_type and false_type)

中似乎几乎相同

所以,我想知道我是否在某处缺少类型名,或者为什么编译器无法将 type 识别为类型。

此外,必须有一种不太复杂的方法来执行此操作。我几乎可以肯定我只是没有看到它。如果有的话,任何人都可以给我一个使用其他方法的类似案例的例子吗?

我在这里看不到 enable_if 的意义(而且您的 enabler 没有做任何有用的事情)。只需使用特化将事件类型映射到所需的函数指针签名,并直接将其用作参数类型:

template<Event> struct event_fn;

template<> struct event_fn<Event::Resize> {
    using type = void (*)(unsigned, unsigned); 
};
// other specializations

// alias template shorthand
template<Event Type> using event_fn_t = typename event_fn<Type>::type;

template<Event Type>
void registerEvent(event_fn_t<Type> f);

与您的方法(修复后)相比,这在处理重载集时更清晰:因为只有一种目标类型,编译器可以选择正确的重载而无需手动消除歧义:

void foo(unsigned, unsigned);
void foo();

registerEvent<Event::Resize>(foo); // OK with the above but not with the original