猪:"enable_if cannot be used to disable this declaration"
SFINAE: "enable_if cannot be used to disable this declaration"
为什么我不能在以下上下文中使用 enable_if
?
我想检测我的模板对象是否有成员函数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):
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):
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
的用途吗?
我放了一个full example here (and cpp.sh link that often fails)
我在 SO 上发现了类似的 Q/As,但通常那些更复杂并且尝试不同的东西。
实例化模板会导致其包含的所有声明的成员实例化。您提供的声明此时只是格式错误。此外,SFINAE 不适用于此处,因为在实例化 class 模板时我们没有解析重载。
您需要使成员具有有效的声明,并确保检查延迟到重载解决。我们可以通过使 notify_exit
本身成为一个模板来做到这两点:
template<typename Q = Queue>
auto notify_exit() -> typename std::enable_if<
has_member_function_notify_exit<Q, void>::value,
void
>::type;
有了 C++20 和概念,你可以使用 requires
:
void notify_exit() requires has_member_function_notify_exit<Queue, void>::value;
当您实例化 MyQueue<std::queue<int>>
时,模板参数 std::queue<int>
被替换到 class 模板中。在导致使用不存在的 typename std::enable_if<false, void>::type
的成员函数声明中。那是一个错误。您不能使用不存在的类型声明函数。
enable_if
的正确使用必须取决于 deduced 的模板参数。在模板参数推导期间,如果用推导的模板参数替换模板参数失败(即 "substitution failure"),那么您不会立即收到错误,它只会导致推导失败。如果推导失败,则该函数不是重载决策的候选者(但仍会考虑任何其他重载)。
但在您的情况下,调用函数时不会推导模板参数,它是已知的,因为它来自周围的 class 模板。这意味着替换失败 是 一个错误,因为在您尝试执行重载解析来调用它之前,函数的声明格式不正确。
您可以通过将函数转换为函数模板来修复您的示例,因此它有一个必须推导的模板参数:
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
,如你所愿。
这个函数可能会被滥用,因为调用者可以调用它作为 notify_exit<SomeOtherType>()
来根据错误的类型欺骗 enable_if
条件。如果调用者这样做,他们应该得到编译错误。
使代码工作的另一种方法是对整个 class 模板进行部分专门化,以便在不需要时简单地删除函数:
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;
};
您可以通过几种不同的方式避免将整个 class 定义重复两次。您可以将所有公共代码提升到基 class 中,并且只在依赖于它的派生 class 中添加 notify_exit()
成员。或者,您可以仅将条件部分移动到基数 class 中,例如:
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();
}
为什么我不能在以下上下文中使用 enable_if
?
我想检测我的模板对象是否有成员函数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):
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):
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
的用途吗?
我放了一个full example here (and cpp.sh link that often fails)
我在 SO 上发现了类似的 Q/As,但通常那些更复杂并且尝试不同的东西。
实例化模板会导致其包含的所有声明的成员实例化。您提供的声明此时只是格式错误。此外,SFINAE 不适用于此处,因为在实例化 class 模板时我们没有解析重载。
您需要使成员具有有效的声明,并确保检查延迟到重载解决。我们可以通过使 notify_exit
本身成为一个模板来做到这两点:
template<typename Q = Queue>
auto notify_exit() -> typename std::enable_if<
has_member_function_notify_exit<Q, void>::value,
void
>::type;
有了 C++20 和概念,你可以使用 requires
:
void notify_exit() requires has_member_function_notify_exit<Queue, void>::value;
当您实例化 MyQueue<std::queue<int>>
时,模板参数 std::queue<int>
被替换到 class 模板中。在导致使用不存在的 typename std::enable_if<false, void>::type
的成员函数声明中。那是一个错误。您不能使用不存在的类型声明函数。
enable_if
的正确使用必须取决于 deduced 的模板参数。在模板参数推导期间,如果用推导的模板参数替换模板参数失败(即 "substitution failure"),那么您不会立即收到错误,它只会导致推导失败。如果推导失败,则该函数不是重载决策的候选者(但仍会考虑任何其他重载)。
但在您的情况下,调用函数时不会推导模板参数,它是已知的,因为它来自周围的 class 模板。这意味着替换失败 是 一个错误,因为在您尝试执行重载解析来调用它之前,函数的声明格式不正确。
您可以通过将函数转换为函数模板来修复您的示例,因此它有一个必须推导的模板参数:
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
,如你所愿。
这个函数可能会被滥用,因为调用者可以调用它作为 notify_exit<SomeOtherType>()
来根据错误的类型欺骗 enable_if
条件。如果调用者这样做,他们应该得到编译错误。
使代码工作的另一种方法是对整个 class 模板进行部分专门化,以便在不需要时简单地删除函数:
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;
};
您可以通过几种不同的方式避免将整个 class 定义重复两次。您可以将所有公共代码提升到基 class 中,并且只在依赖于它的派生 class 中添加 notify_exit()
成员。或者,您可以仅将条件部分移动到基数 class 中,例如:
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();
}