从属名称不是类型,带有 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
我正在尝试注册这样的事件:
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