为什么在以下情况下不能使用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;
我不断得到(c语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(和cpp.sh link that often fails)
我在SO上发现了类似的Q / A,但通常这些问题更为复杂,并尝试不同的方法。
答案 0 :(得分:4)
实例化sh index.sh > timezones.json
时,模板参数MyQueue<std::queue<int>>
被替换为类模板。在导致使用std::queue<int>
的成员函数声明中,该函数不存在。那是一个错误。您不能使用不存在的类型来声明函数。
正确使用typename std::enable_if<false, void>::type
必须取决于模板参数deduced。在模板自变量推导过程中,如果将推导的模板自变量替换为模板参数失败(即“替代失败”),那么您不会立即得到错误,只会导致推导失败。如果推导失败,则该函数不是重载解决方案的候选者(但仍将考虑其他重载)。
但是在您的情况下,调用函数时不推导出template参数,因为它来自周围的类模板,所以它是已知的。这意味着替换失败 是一个错误,因为该函数的声明是错误的,甚至在尝试执行重载解析来调用它之前。
您可以通过将函数转换为函数模板来修复示例,因此它具有必须推导的模板参数:
enable_if
此处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>()
的条件取决于{{1} },就像您最初想要的那样。
此函数可能被滥用,因为调用者可以根据错误的类型将其作为enable_if
来欺骗Queue
条件。如果调用者这样做,他们应该得到编译错误。
使代码起作用的另一种方法是对整个类模板进行部分专业化处理,以在不需要时简单地删除该函数:
notify_exit<SomeOtherType>()
您可以避免以几种不同的方式重复整个类的定义两次。您可以将所有通用代码提升到基类中,而仅在依赖它的派生类中添加enable_if
成员。或者,您可以仅将条件部分移到基类中,例如:
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;
};
答案 1 :(得分:3)
实例化模板会导致其包含的所有声明的成员实例化。此时,您提供的声明格式不正确。此外,SFINAE不适用于此处,因为在实例化类模板时我们不会解决重载。
您需要使成员成为带有有效声明的内容,并确保检查延迟到重载解析为止。我们可以通过将notify_exit
本身作为模板来完成这两者:
template<typename Q = Queue>
auto notify_exit() -> typename std::enable_if<
has_member_function_notify_exit<Q, void>::value,
void
>::type;
答案 2 :(得分:3)
使用C ++ 20和概念,您可以使用requires
:
void notify_exit() requires has_member_function_notify_exit<Queue, void>::value;