【问题标题】:dependent name is not a type, templates with SFINAE从属名称不是类型,带有 SFINAE 的模板
【发布时间】:2018-06-28 06:49:53
【问题描述】:

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

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...));

这种方法会引发几个语法错误(因为“此处的错误”标记行并贯穿整个堆栈跟踪),并发出警告,告诉:

'validate_event&lt;__formal,&lt;unnamed-symbol&gt;...&gt;::value': dependent name is not a type.

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

还尝试使用value 而不是type,因为它们在bool 类型的integral constants 中似乎几乎相同(如true_type 和false_type)

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

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

【问题讨论】:

  • Event::Resize 不是类型,它是Event 类型的值,即您在registerEvent 中使用的Type,因此它永远无法匹配validate_event&lt;Event::Resize, ...&gt;跨度>
  • validate_event&lt;Event::Resize, ...&gt; 的唯一出现是 template&lt;Event, typename...&gt; struct validate_event... 的规范,仅当 Event 为 Event::Resize 时才会调用。您可以为专业化添加值
  • 好的,所以Type 应该是Event::Resize...但是type 是什么?您在 registerEvent 签名中使用 Type 作为类型,但它不是,它是一个值。当Type 是一个值时,Type type 没有任何意义。
  • 你需要采用register&lt;Event::Resize&gt;(callback) 语法才能做你想做的事。
  • 删除Type type应该可以,编译器可以很容易地从函数指针中推断出Args

标签: c++ c++11 templates c++14 sfinae


【解决方案1】:

我在这里看不到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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-11
    • 2019-03-29
    • 1970-01-01
    相关资源
    最近更新 更多