检查 class 中函数是否存在的概念(GCC 中的问题?)

concept to check existence of function in class (problem in GCC?)

我想检测 class 中是否存在一个函数(在我的例子中是 operator()),而不管它的签名或是否可以获得指向它的指针(没有附加信息可能是不可能的,因为它是模板化的或超载的)。以下代码使用一个概念在 MSVC 和 clang 上编译,但不是 GCC(请参阅下面的 godbolt link 以获取错误消息)。这是否应该工作并且 GCC 不符合要求,或者这是否应该工作并且 MSVC 和 clang 是否过于宽松?有趣的是,GCC 不仅对重载和模板化 operator()s 失败,而且对简单仿函数也失败。

另请注意,虽然示例代码使用了采用 int 的一元函数的变体,但我希望无论函数签名如何(以及它对 MSVC 和 clang 的作用)如何,这个概念都能起作用。

在此处尝试 GCC, clang and MSVC

上下文使 this 工作,它现在在 MSVC 和 clang 上工作,但不是 GCC。

template <typename C>
concept HasCallOperator = requires(C t)
{
    t.operator();
};

struct functor
{
    int operator()(int in_)
    { return 1; }
};

struct functorOverloaded
{
    int operator()(const int& in_)
    { return 1; }
    int operator()(int&& in_)
    { return 1; }
};

struct functorTemplated
{
    template <typename... T>
    int operator()(const T&... in_)
    { return 1; }
};

template<HasCallOperator T>
struct B {};


int main()
{
    B<functor> a;
    B<functorOverloaded> b;
    B<functorTemplated> c;
}

首先,检查一个概念的方法只是static_assert(而不是尝试实例化一个受约束的class模板)。

static_assert(HasCallOperator<functor>);
static_assert(HasCallOperator<functorOverloaded>);
static_assert(HasCallOperator<functorTemplated>);

其次,你不能写 t.operator() 的原因与你不能为任何其他非静态成员函数写 f.fun 的原因相同:如果你写 class 成员访问,它必须以调用结束。所以这只是一个 clang/msvc 错误,它允许任何一个。

如果调用运算符或函数模板(或两者)重载,&C::operator() 将不起作用。

这确实对整个问题提出了质疑,因为如果不进行反思,我们对这些问题所能给出的答案种类非常有限。您实际上只能解决非重载、非模板调用运算符的简单情况。


不过,有一种方法在这里行得通。诀窍与我们在当前情况下遇到的问题相同:如果 operator() 过载,&C::operator() 不起作用。

所以我们要做的是构建一个 &C::operator() 被重载的情况 if 有一个,然后反转支票。即:

#include <type_traits>

struct Fake { void operator()(); };
template <typename T> struct Tester : T, Fake { };

template <typename C>
concept HasCallOperator = std::is_class_v<C> and not requires(Tester<C> t)
{
    &Tester<C>::operator();
};

HasCallOperator<C> 不检查 C,它检查从 C 继承的类型和具有非重载非模板调用运算符的类型。如果 &Tester<C>::operator() 是一个有效的表达式,那就意味着它引用了 &Fake::operator(),这意味着 C 没有。如果 C 有调用运算符(无论是重载还是模板或两者兼而有之),那么 &Tester<C>::operator() 将是不明确的。

is_class_v 检查是为了确保像 HasCallOperator<int> 这样的东西是 false 而不是错误的。

请注意,这不适用于 final classes.