SFINAE:“不能使用enable_if禁用此声明” [英] SFINAE: "enable_if cannot be used to disable this declaration"

查看:130
本文介绍了SFINAE:“不能使用enable_if禁用此声明”的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么在以下情况下不能使用 enable_if

Why can I not use enable_if in the following context?

我想检测是否我的模板对象具有成员函数 notify_exit

I'd like to detect whether my templated object has the member function notify_exit

template <typename Queue>
class MyQueue
{
   public:
    auto notify_exit() -> typename std::enable_if<
            has_member_function_notify_exit<Queue, void>::value,
            void
        >::type;

    Queue queue_a;
};

初始化为:

MyQueue<std::queue<int>> queue_a;

我一直在(clang 6):

I keep getting (clang 6):

example.cpp:33:17: error: failed requirement 'has_member_function_notify_exit<queue<int, deque<int, allocator<int> > >, void>::value';
      'enable_if' cannot be used to disable this declaration
            has_member_function_notify_exit<Queue, void>::value,

或(g ++ 5.4):

or (g++ 5.4):

In instantiation of 'class MyQueue<std::queue<int> >':
33:35:   required from here
22:14: error: no type named 'type' in 'struct std::enable_if<false, void>'

我尝试了很多不同的方法,但不知道为什么我不能使用 enable_if 禁用此功能。这不是 enable_if 的全部用途吗?

I've tried a bunch of different things, but can't figure out why I can't use enable_if to disable this function. Isn't this exactly what enable_if is for?

我已经放置了此处的完整示例(以及 cpp.sh链接经常失败

我在SO上发现了类似的Q / A,但通常情况下,它们比较复杂,并且尝试使用不同的方法。 / p>

I've found similar Q/As on SO, but generally those were more complicated and attempting something different.

推荐答案

实例化 MyQueue< std :: queue< int>> 模板参数 std :: queue< int> 被替换为类模板。在导致使用 typename std :: enable_if< false,void> :: type 的成员函数声明中,该函数不存在。那是一个错误。您不能使用不存在的类型声明函数。

When you instantiate MyQueue<std::queue<int>> the template argument std::queue<int> gets substituted into the class template. In the member function declaration that leads to a use of typename std::enable_if<false, void>::type which does not exist. That's an error. You can't declare a function using a type that doesn't exist.

正确使用 enable_if 必须取决于在推论的模板参数上。在模板参数推导过程中,如果将推导的模板参数替换为template参数失败(即替代失败),那么您不会立即得到错误,只会导致推导失败。如果推导失败,则该函数不是重载解决方案的候选者(但仍将考虑其他重载)。

Correct uses of enable_if must depend on a template parameter that is deduced. During template argument deduction, if substituting the deduced template argument for the template parameter fails (i.e. a "substitution failure") then you don't get an immediate error, it just causes deduction to fail. If deduction fails, the function isn't a candidate for overload resolution (but any other overloads will still be considered).

但是在您的情况下,不会推导template参数调用该函数时,它是已知的,因为它来自周围的类模板。这意味着替换失败是一个错误,因为该函数的声明是错误的,甚至在尝试执行重载解析之前都无法对其进行调用。

But in your case the template argument is not deduced when calling the function, it's already known because it comes from the surrounding class template. That means that substitution failure is an error, because the function's declaration is ill-formed before you even try to perform overload resolution to call it.

您可以通过将函数变成函数模板来修复示例,因此它必须具有一个模板参数:

You can fix your example by turning the function into a function template, so it has a template parameter that must be deduced:

template<typename T = Queue>
  auto notify_exit() -> typename std::enable_if<
              has_member_function_notify_exit<T, void>::value,
              void
          >::type;

此处 enable_if 的条件取决于 T 而不是 Queue ,所以 :: type 成员是否存在直到您尝试将模板参数替换为 T 为止,还是未知。函数模板具有默认的模板参数,因此,如果您仅调用 notify_exit()而没有任何模板参数列表,则等效于 notify_exit< Queue> (),这意味着 enable_if 条件取决于您最初想要的 Queue

Here the enable_if condition depends on T instead of Queue, so whether the ::type member exists or not isn't known until you try to substitute a template argument for T. The function template has a default template argument, so that if you just call notify_exit() without any template argument list, it's equivalent to notify_exit<Queue>(), which means the enable_if condition depends on Queue, as you originally wanted.

此函数可能会被误用,因为调用者可以将其作为 notify_exit< SomeOtherType>()来欺骗 enable_if 条件取决于错误的类型。如果调用者这样做,他们应该得到编译错误。

This function can be misused, as callers could invoke it as notify_exit<SomeOtherType>() to trick the enable_if condition into depending on the wrong type. If callers do that they deserve to get compilation errors.

使代码起作用的另一种方法是对整个类模板进行部分专业化处理,以简单地删除

Another way to make the code work would be to have a partial specialization of the entire class template, to simply remove the function when it's not wanted:

template <typename Queue,
          bool Notifiable
            = has_member_function_notify_exit<Queue, void>::value>
class MyQueue
{
  public:
    void notify_exit();

    Queue queue_a;
};

// partial specialization for queues without a notify_exit member:
template <typename Queue>
class MyQueue<Queue, false>
{
  public:
    Queue queue_a;
};

您可以避免以几种不同的方式重复两次整个类的定义。您可以将所有通用代码提升为基类,而在依赖于此的派生类中仅添加 notify_exit()成员。另外,您也可以只将条件部分移入基类,例如:

You can avoid repeating the whole class definition twice in a few different ways. You could either hoist all the common code into a base class and only have the notify_exit() member added in the derived class that depends on it. Alternatively you can move just the conditional part into a base class, for example:

template <typename Queue,
          bool Notifiable
            = has_member_function_notify_exit<Queue, void>::value>
class MyQueueBase
{
  public:
    void notify_exit();
};

// partial specialization for queues without a notify_exit member:
template <typename Queue>
class MyQueueBase<Queue, false>
{ };

template<typename Queue>
class MyQueue : public MyQueueBase<Queue>
{
public:
  // rest of the class ...

  Queue queue_a;
};

template<typename Queue, bool Notifiable>
void MyQueueBase<Queue, Notifiable>::notify_exit()
{
  static_cast<MyQueue<Queue>*>(this)->queue_a.notify_exit();
}

这篇关于SFINAE:“不能使用enable_if禁用此声明”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆