【问题标题】:is_invocable with arbitrary function argument typesis_invocable 具有任意函数参数类型
【发布时间】:2019-07-06 10:34:54
【问题描述】:

有没有办法将std::is_invocable 与任意函数参数类型一起使用,例如:std::is_invocable<Function, auto>。这个想法是检查 Function 是否可以接受 1 个参数,而不管参数的类型如何。对于用例,请考虑两个 lambda:auto lambda1 = [](auto x) {...}auto lambda2 = [](auto x, auto y) {...} 和一个更高阶的模板函数:

// specialize for 1 argument
template<typename Function, std::enable_if_t<(std::is_invocable<Function, auto>::value && !std::is_invocable<Function, auto, auto>::value)>, bool> = true>
void higherOrderFunc(Function&& func);

// specialize for 2 arguments
template<typename Function, std::enable_if_t<std::is_invocable<Function, auto, auto>::value, bool> = true>
void higherOrderFunc(Function&& func);

第一种情况下的!std::is_invocable&lt;Function, auto, auto&gt;::value 是为了防止重载函数出现歧义(也就是说,在这种情况下,首选的特化是在出现歧义时使用第二个参数)。

请注意,我知道在这种情况下不能像这样使用auto。我在问是否有办法实现这种行为(至少部分)。

【问题讨论】:

  • 似乎没有任何方法可以检查这一点。为什么需要?
  • 恐怕这是不可能的,因为它需要将所有类型都替换到函数模板中。您可以拥有仅对模板参数的某些组合有效的函数模板,并且这些条件可以任意复杂。请注意,只能调用函数,不能调用模板。这可能是什么用例?最后,你还是想使用一些具体的类型,那么,为什么不检查它们呢?
  • @Quimby 假设您有 100 种类型,手动为所有这些类型编写检查几乎不切实际。整个事情都与实现高阶函数有关。
  • 不确定如何帮助您,可调用性可能取决于其参数。即使template&lt;typename T&gt; foo(T,T) 对这两个参数的所有类型都无效。并且对模板没有限制。函数本身可能被重载,所以 C++ 只能回答“如果我把这些具体参数放在那里,func(args...) 是有效的表达式吗?”。类和函数模板都在实例化时使用具体类型进行检查,因此编译器可以开始将可用模板与这些具体类型进行匹配。你问的是相反的问题。
  • 您能否举一个更具体的例子,在某物上使用高阶函数?不必是可编译的 C++。

标签: c++ c++17 sfinae type-deduction invocable


【解决方案1】:

也许不是一个完美的解决方案...但您可以尝试passepartout

struct passepartout
 {
   template <typename T>
   operator T & ();

   template <typename T>
   operator T && ();
 };

注意转换操作符只是声明的,没有定义的;所以这个结构可以在decltype()std::declval()(和std::is_invocable)中使用,但不能被实例化。

现在您可以编写您的 higherOrderFunc 传递引用到 passepartoutstd::is_invocable

template <typename F,
   std::enable_if_t<
           std::is_invocable_v<F, passepartout &>
      && ! std::is_invocable_v<F, passepartout &, passepartout &>, bool>
         = true>
void higherOrderFunc (F)
 { std::cout << "-- one parameter callable" << std::endl; }

template <typename F,
   std::enable_if_t<
      std::is_invocable_v<F, passepartout &, passepartout &>, bool> = true>
void higherOrderFunc (F)
 { std::cout << "-- two parameter callable" << std::endl; }

诀窍在于,如果一个可调用对象等待auto(或auto &amp;,或auto &amp;&amp;),则类型被推断为passepartout 本身;当可调用等待特定类型(int,在以下示例中,有或没有引用)时,模板operator T &amp; ()(或operator T &amp;&amp; (),根据情况)与预期类型兼容(在某种意义上)。

以下是完整的编译示例

#include <type_traits>
#include <iostream>

struct passepartout
 {
   template <typename T>
   operator T & ();

   template <typename T>
   operator T && ();
 };

template <typename F,
   std::enable_if_t<
           std::is_invocable_v<F, passepartout &>
      && ! std::is_invocable_v<F, passepartout &, passepartout &>, bool>
         = true>
void higherOrderFunc (F)
 { std::cout << "-- one parameter callable" << std::endl; }

template <typename F,
   std::enable_if_t<
      std::is_invocable_v<F, passepartout &, passepartout &>, bool> = true>
void higherOrderFunc (F)
 { std::cout << "-- two parameter callable" << std::endl; }

int main ()
 {
   auto l1a = [](auto &&){};
   auto l1b = [](int &){};
   auto l2a = [](auto &, int &&){};
   auto l2b = [](auto, int const &){};
   auto l2c = [](auto &&, auto const &){};
   auto l2d = [](int &&, auto const &, auto && ...){};

   higherOrderFunc(l1a);
   higherOrderFunc(l1b);
   higherOrderFunc(l2a);
   higherOrderFunc(l2b);
   higherOrderFunc(l2c);
   higherOrderFunc(l2c);
   higherOrderFunc(l2d);
 }

【讨论】:

  • 虽然这确实是部分解决方案,但它与 florestan 的 arbitrary_t (stackoverflow.com/questions/25704173/arity-of-a-generic-lambda) 存在相同的问题,需要在 lambda 内部进行强制转换。尝试例如auto l2a = [](auto x, auto y) {return x+y;};
  • @lightxbulb - 是的...仅对decltype() 类似用途有用(如std::is_invocable)并且(我想)期望更多的东西是不合理的。
  • @lightxbulb - 删除运营商实现可能有意义:decltype()std::is_invocable 继续工作,直接使用 passspartout 的诱惑减少了。但仍然是auto &amp; 问题。
  • @lightxbulb - 我修改了答案,删除了转换运算符定义并将引用传递给std::is_invocable;这大大简化了答案,也应该解决auto &amp; 问题。
  • 感谢您这样做。虽然它并不直接适用于我的用例(因为我实际上不想强制转换为 lambda),但它确实是一个部分解决方案,所以我会接受它作为答案,考虑到 C++ 目前没有'似乎提供了一种机制来实现我想要的。希望在标准的未来迭代中,在某些时候甚至能够查询特定函数的所有重载的数量和类型(例如作为元组的静态数组返回)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-15
  • 1970-01-01
  • 2010-10-04
相关资源
最近更新 更多