Mat*_*att 11 c++ sfinae c++11 clang++
为什么我不能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;
};
Run Code Online (Sandbox Code Playgroud)
初始化为:
MyQueue<std::queue<int>> queue_a;
Run Code Online (Sandbox Code Playgroud)
我不断收到(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,
Run Code Online (Sandbox Code Playgroud)
或(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>'
Run Code Online (Sandbox Code Playgroud)
我尝试了很多不同的东西,但不知道为什么我不能enable_if用来禁用这个功能。这不正是enable_if为了什么吗?
我在这里放了一个完整的例子(以及经常失败的 cpp.sh 链接)
我在 SO 上发现了类似的 Q/As,但通常那些更复杂并尝试不同的东西。
Jon*_*ely 13
当您实例化MyQueue<std::queue<int>>模板参数时,它std::queue<int>会被替换到类模板中。在导致typename std::enable_if<false, void>::type不存在使用的成员函数声明中。那是一个错误。您不能使用不存在的类型声明函数。
enable_ifmust 的正确使用取决于推导出的模板参数。在模板参数推导期间,如果将推导的模板参数替换为模板参数失败(即“替换失败”),那么您不会立即得到错误,它只会导致推导失败。如果推导失败,则该函数不是重载决议的候选对象(但仍会考虑任何其他重载)。
但是在您的情况下,在调用函数时不会推导出模板参数,它是已知的,因为它来自周围的类模板。这意味着替换失败是一个错误,因为在您甚至尝试执行重载解析来调用它之前,函数的声明是格式错误的。
您可以通过将函数转换为函数模板来修复您的示例,因此它具有必须推导出的模板参数:
template<typename T = Queue>
auto notify_exit() -> typename std::enable_if<
has_member_function_notify_exit<T, void>::value,
void
>::type;
Run Code Online (Sandbox Code Playgroud)
这里的enable_if条件取决于T而不是Queue,因此在::type您尝试将模板参数替换为 之前,不知道该成员是否存在T。函数模板有一个默认的模板参数,所以如果你只是在notify_exit()没有任何模板参数列表的情况下调用,它等价于notify_exit<Queue>(),这意味着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;
};
Run Code Online (Sandbox Code Playgroud)
您可以通过几种不同的方式避免将整个类定义重复两次。您可以将所有公共代码提升到一个基类中,并且只notify_exit()在依赖它的派生类中添加成员。或者,您可以仅将条件部分移动到基类中,例如:
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();
}
Run Code Online (Sandbox Code Playgroud)
使用 C++20 和概念,您可以使用requires:
void notify_exit() requires has_member_function_notify_exit<Queue, void>::value;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3734 次 |
| 最近记录: |