检查是否存在operator()

时间:2019-06-22 18:55:02

标签: c++ template-meta-programming sfinae

我需要一个类型特征HasCall来检查T的以下类型实例的有效性:

template<class T> struct Caller: T
{
    using T::operator();
};

在C ++ 14中有没有办法做到这一点? 这是我的尝试,但是不起作用: https://godbolt.org/z/vxgJCR

编辑

我了解SFINAE及其运作方式。这个问题比仅检查某些表达式的有效性更为困难。 我希望这些断言能够通过:

struct A {void operator()(int) {}};
static_assert(HasCall<A>, "Check must work with any set of arguments.");

struct B {void operator()() {}};
static_assert(HasCall<B>, "Check must work with any set of arguments.");

struct C {template<typename... Args> void operator()(Args&&...) {}};
static_assert(HasCall<C>, "Templated operators must be detected correctly.");

struct D {};
static_assert(!HasCall<D>, "No operator() at all.");

static_assert(!HasCall<void(*)()>, "Class cannot inherit from function pointers.");

检查表达式&T :: operator()的有效性是不够的,因为它不适用于重载的或模板的operator()。

请用这些断言检查您的解决方案。

这个问题不是重复的。

2 个答案:

答案 0 :(得分:4)

尝试一下:

template <typename T, typename = void> struct has_operator {
    enum { value = 0 };
};
// thanks super for great suggestion!
template <typename T> struct has_operator<T, std::void_t<decltype(std::declval<T>()())>> {
    enum { value = 1 };
};
template<class T, typename = std::enable_if<has_operator<T>::value>> struct Caller: T
{
    using T::operator();
};

这是SFINAE的原理-模板实例化中的一些错误而不是整个编译失败都会使编译器忽略给定的实例化。因此,首先我们用has_operator定义value = 0,这是我们的“默认值”。然后,我们对类型T的模板进行特殊化处理。现在,我们希望仅当T::operator()存在时才选择这种特殊化处理。因此,我们添加了第二个模板参数,并将其默认值设置为decltype(&T::operator())value = 1。我们在这里不关心实型,我们关心的是如果T::operator()存在,则可以正常编译。编译器将为此T选择此专业化。如果不存在,编译器将忽略这种专业化,并选择具有has_operator的“默认” value = 0

所以现在我们有了struct has_operator,当它像这样使用时:has_operator<T>::value将产生常数0,当T没有operator ()时(请注意任何参数)和值1,何时拥有。您可以将其与std::enable_if一起使用(顺便说一句,两者的工作原理几乎相同)。

可以通过这种技术应用的事物列表相当长-几乎可以使用可以进行编译的任何事物。

答案 1 :(得分:4)

不,没有办法。

所有解决方案都需要知道调用的签名(准确或兼容),或者不依赖任何重载。

实际上,即使您具有已知的签名,也无法可靠地检测到任何重载或模板调用运算符,因为隐式强制转换为函数指针可以欺骗declval测试。

您将不得不找到解决问题的另一种方法,或者等待反思。