检查 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 的作用)如何,这个概念都能起作用。
上下文使 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.
我想检测 class 中是否存在一个函数(在我的例子中是 operator()
),而不管它的签名或是否可以获得指向它的指针(没有附加信息可能是不可能的,因为它是模板化的或超载的)。以下代码使用一个概念在 MSVC 和 clang 上编译,但不是 GCC(请参阅下面的 godbolt link 以获取错误消息)。这是否应该工作并且 GCC 不符合要求,或者这是否应该工作并且 MSVC 和 clang 是否过于宽松?有趣的是,GCC 不仅对重载和模板化 operator()
s 失败,而且对简单仿函数也失败。
另请注意,虽然示例代码使用了采用 int
的一元函数的变体,但我希望无论函数签名如何(以及它对 MSVC 和 clang 的作用)如何,这个概念都能起作用。
上下文使 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.