C++ 让SFINAE在重载函数对象上使用'is_callable'

C++ 让SFINAE在重载函数对象上使用'is_callable',c++,templates,c++14,sfinae,c++17,C++,Templates,C++14,Sfinae,C++17,考虑以下函数对象l: auto l = [](auto x){ x.foo(); }; 我可以成功地static\u断言它可以用具有.foo()成员函数的类型调用: struct Foo { void foo(); }; static_assert(std::is_callable<decltype(l)(Foo)>{}); static_assert(!std::is_callable<decltype(l)(int)>{}); 不幸的是,上述static_ass

考虑以下函数对象
l

auto l = [](auto x){ x.foo(); };
我可以成功地
static\u断言它可以用具有
.foo()
成员函数的类型调用:

struct Foo { void foo(); };
static_assert(std::is_callable<decltype(l)(Foo)>{});
static_assert(!std::is_callable<decltype(l)(int)>{});
不幸的是,上述
static_assert
导致编译错误:

prog.cc: In instantiation of '<lambda(auto:1)> [with auto:1 = int]':
prog.cc:8:44:   required by substitution of 'template<class TF, class ... Ts> 
                struct is_callable<TF(Ts ...), 
                    std::void_t<decltype (declval<TF>()((declval<Ts>)()...))> 
                > [with TF = <lambda(auto:1)>; Ts = {int}]'
prog.cc:17:50:   required from here
prog.cc:12:24: error: request for member 'foo' in 'x', which is of non-class type 'int'
 auto l = [](auto x){ x.foo(); };
                      ~~^~~
template <typename, typename = void>
struct is_callable : std::false_type { };

template <typename TF, class... Ts>
struct is_callable<TF(Ts...),
    std::void_t<decltype(std::declval<TF>()(std::declval<Ts>()...))>>
    : std::true_type { };
我的印象是,如果
std::void_t
中的表达式由于SFINAE而无效,则将选择
std::false_类型
回退

  • 为什么不在此处执行此SFINAE,从而导致编译错误?

  • 如何实现所需的行为?(即,如果调用重载函数对象的格式不正确,则计算为
    std::false\u type

    • 请注意,在我的实际用例中,我将无法访问正在执行
      is\u callable
      检查的函数对象的实现

您的lambda不约束它的​ 与sfinae的争论。您的lambda可以用任何东西调用,但在用这些参数实例化时会触发硬编译错误

要获得所需效果,请对返回类型设置约束:

auto l = [](auto x) -> void_t<decltype(x.foo())> { x.foo(); };
autol=[](autox)->void_{x.foo();};
这样,
是可调用的
特性将产生正确的结果

为什么这个SFINAE没有在这里发生,导致编译错误

因为
l
可以调用任何东西,所以编译错误发生在其主体被实例化时,这发生在SFINAE发生的“隔离环境”之外

约束lambda将解决此问题:

auto l = [](auto x) -> void_t<decltype(x.foo())> { x.foo(); };
autol=[](autox)->void_{x.foo();};
(注意,
void\u t
在这里被用作
x.foo()
不会被lambda返回。)


我怎样才能达到我想要的行为? (即,如果调用重载函数对象的格式不正确,则计算为
std::false_type

如果函数对象的签名没有得到适当的约束,就无法实现所需的行为

要做到这一点,编译器需要实现某种“推测性编译”,这是不可取的,因为实现起来很困难

本文中的详细信息:

<>没有办法在C++内部检查它,而且不会有任何:它已经被明确地安排了,编译器供应商不能强制执行一个“推测编译”和从任意深层模板实例化失败中回溯。
在这次讨论中:。

啊,我现在理解了这个问题-谢谢。有没有办法在不约束lambda的情况下实现我想要的行为?@VittorioRomeo你是在问是否有办法在不约束lambda的情况下约束lambda。@巴里:我是在问是否有可能检测到无约束泛型lambda体的实例化是否格式不正确(没有产生一个硬错误)没关系——在考虑了它并阅读了相关材料后,我意识到我基本上是在要求“推测性编译”这可能是C++的一部分。为什么纪尧姆的文章很好地回答了我的第一个问题,而不是第二个问题。他怎么回答不出你的第二个问题?那就是<代码> -VoIDytt < /Cult>部分。这个回答主要是重复他的,只是用更大的格式。“巴里:我的第二个问题是UNC。lear-我将更新OP。我想检测任意重载/模板函数对象的实例化是否格式错误。当然,在我的最小示例中,我可以约束lambda-但这并不总是我真正的问题。@Barry:添加约束让我明白了为什么会出现硬错误然而,这也让我怀疑是否有任何方法可以通过“看身体”来达到同样的行为函数对象,这更接近我的实际用例。我在回答中找到并链接的文章/讨论解释了为什么这不可能。我发现这些信息非常有价值,因此我决定为我自己的问题添加另一个答案。