为什么std :: is_invocable不能与自动推导返回类型的模板化operator()一起使用(例如,通用lambda) [英] Why doesn't std::is_invocable work with templated operator() which return type is auto-deduced (eg. generic lambdas)

查看:84
本文介绍了为什么std :: is_invocable不能与自动推导返回类型的模板化operator()一起使用(例如,通用lambda)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

c ++ 17引入了 template< class Fn,class..ArgTypes>struct is_invocable :

c++17 introduces template <class Fn, class...ArgTypes> struct is_invocable:

确定是否可以使用参数 ArgTypes ... 调用 Fn .形式上,确定当被视为未评估的操作数时, INVOKE(declval< Fn>(),declval< ArgTypes>()...)是否格式正确,其中 INVOKE 是在 Callable 中定义的操作.

Determines whether Fn can be invoked with the arguments ArgTypes.... Formally, determines whether INVOKE(declval<Fn>(), declval<ArgTypes>()...) is well formed when treated as an unevaluated operand, where INVOKE is the operation defined in Callable.

但是,此模板不适用于自动推导(直接或间接)返回类型的 templateed operator():

However, this template does not work with templated operator() which (direct or indirect) return type is auto-deduced:

#include <type_traits>
#include <iostream>

struct A {
  int a() { return 1; }
};

struct B {};

struct {
  template<typename T>
  auto operator()(T t) { return t.a(); }
} f1;

struct {
  template<typename T>
  auto operator()(T t) -> decltype(t.a()) { return t.a(); }
} f2;

struct {
  template<typename T>
  auto operator()(T t) -> decltype(f1(t)) { return f1(t); }
} f3;

template<typename F, typename T>
void check(F&& f, T) {
  std::cout << std::boolalpha << std::is_invocable_v<F, T> << std::endl;
}

int main() {
  check(f1, A());   // true
  check(f2, A());   // true
  check(f3, A());   // true
  //check(f1, B()); // error: ‘struct B’ has no member named ‘a’
  check(f2, B());   // false
  //check(f3, B()); // error: ‘struct B’ has no member named ‘a’
  return 0;
}

我想原因可能与SFINAE有关.但这仍然不是直观的.我尝试检查 N4659草案的引入了 std的段落::is_invocable ,但仍找不到有关此行为的更多详细信息.由于我不是这方面的专家,因此可能有遗漏之处.

I guess the reason may be related to SFINAE. But this is still not intuitive. I tried to check N4659 draft's paragraphs which introduced std::is_invocable, but still couldn't find more detail about this behaivor. Since I am not an expert in this area, there may be omissions.

推荐答案

我想原因可能与SFINAE有关.

I guess the reason may be related to SFINAE.

的确.我们称 f1 为"SFINAE-unfriendly":

Indeed. We call things like f1 "SFINAE-unfriendly":

struct {
  template<typename T>
  auto operator()(T t) { return t.a(); }
} f1;

那是因为 f1 宣称自己可以被任何东西调用(完全没有约束),但是要弄清楚调用操作符实际返回的内容,您必须实例化调用运算符的主体.这涉及确定表达式 t.a()的类型,但是在这一点上,我们不在即时上下文"的范围之内.实例化.此时的任何失败都不是替代失败,而是硬编译错误.

That's because f1 advertises itself as being invocable with anything (there are no constraints at all) but to find out what the call operator actually returns, you have to instantiate the body of the call operator. That involves determining the type of the expression t.a(), but at this point we're outside of the "immediate context" of the instantiation. Any failure at this point is not a substitution failure - it's a hard compile error.

f2 :

struct {
  template<typename T>
  auto operator()(T t) -> decltype(t.a()) { return t.a(); }
} f2;

对SFINAE友好.对 t.a()的检查发生在替换的直接上下文中,因此该表达式中的错误导致该函数仅从候选集中删除. is_invocable 可以检查并确定 false .

is SFINAE-friendly. The check for t.a() happens in the immediate context of the substitution, so an error in that expression leads to the function simply being removed from the candidate set. is_invocable can check this and determine false.

f3 f1 相同-当我们在即时上下文中检查 f1(t))时,的实际分辨率> decltype(f1(t))仍在即时上下文之外,因此它仍然是硬编译器错误.

f3 is the same as f1 - while we check f1(t)) in the immediate context, the actual resolution of decltype(f1(t)) is still outside the immediate context, so it's still a hard compiler error.

简短的版本是:除非您对SFINAE友好,否则任何类型特征或概念都不起作用.任何失败都必须在立即发生的情况下进行.

The short version is: no type traits or concepts work unless you're SFINAE-friendly. Any failures must be in the immediate context.

这篇关于为什么std :: is_invocable不能与自动推导返回类型的模板化operator()一起使用(例如,通用lambda)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆