【问题标题】:Why std::is_function returns false for simple functions and lambdas?为什么 std::is_function 对简单函数和 lambdas 返回 false?
【发布时间】:2016-06-21 14:08:09
【问题描述】:

有以下代码:

#include <iostream>
#include <type_traits>

template <typename F,
          typename = typename std::enable_if<
                                              std::is_function< F >::value
                                            >::type>
int fun( F f ) // line 8
{
  return f(3);
}

int l7(int x)
{
  return x%7;
}

int main()
{
  auto l = [](int x) -> int{
    return x%7;
  };
  fun(l);  // line 23
  //fun(l7); this will also fail even though l7 is a regular function

  std::cout << std::is_function<decltype(l7)>::value ; // prints 1
}

我会得到以下错误:

main2.cpp: In function ‘int main()’:
main2.cpp:23:8: error: no matching function for call to ‘fun(main()::<lambda(int)>&)’
   fun(l);
        ^
main2.cpp:8:5: note: candidate: template<class F, class> int fun(F)
 int fun( F f )
     ^
main2.cpp:8:5: note:   template argument deduction/substitution failed:
main2.cpp:5:11: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
           typename = typename std::enable_if<
           ^

当我注释掉 std::enable_if 模板参数时,它编译并运行得很好。为什么?

【问题讨论】:

  • std::is_function 仅检查不包括 lambdas 的函数类型。您是否有理由需要使用 SFINAE?如果你真的想做检查,你可以检查f(3) 是否格式正确,而不是检查f 是否类似于函数
  • 看起来您实际上是在寻找类似 @​​987654321@ 的东西,它可以在 C++17 中使用,但“可以用纯 C++ 实现 std::is_convertiblestd::result_of” 11.
  • @TartanLlama 你如何检查f(3) 的格式是否正确?
  • @Patryk 类似this。这是expression SFINAE

标签: c++ c++11 lambda template-meta-programming typetraits


【解决方案1】:

来自cppreference

检查 T 是否为函数类型。 std::function、lambdas、具有重载 operator() 的类和指向函数的指针等类型不计入函数类型。

This answer 解释说,您还需要使用std::remove_pointer&lt;F&gt;::type 作为类型,因为函数在按值传递时会转换为指向函数的指针。所以你的代码应该是这样的:

template <typename F,
          typename = typename std::enable_if<
                                              std::is_function<
                                                typename std::remove_pointer<F>::type
                                              >::value
                                            >::type>
int fun( F f )
{
  return f(3);
}

【讨论】:

  • 所以至少f(l7) 应该可以工作,对吧?由于l7 是一个常规函数
  • 正确,从您的代码中您已经看到std::is_function&lt;decltype(l7)&gt;的正确输出
  • 你需要一个std::remove_pointer&lt;F&gt; 因为F 实际上是一个指向函数的指针。我会更新我的答案
  • 你是对的。我已经用std::remove_pointer&lt;F&gt; 进行了检查,但是对于 lambda 而不是函数指针。
  • @Kevin 函数可以通过引用传递吗?
【解决方案2】:

解决这个问题的另一种方法是编写更具体的类型特征。例如,这个检查参数类型是否可转换并且适用于任何可调用的内容。

#include <iostream>
#include <type_traits>
#include <utility>
#include <string>

template<class T, class...Args>
struct is_callable
{
    template<class U> static auto test(U*p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type());

    template<class U> static auto test(...) -> decltype(std::false_type());

    static constexpr auto value = decltype(test<T>(nullptr))::value;
};

template<class T, class...Args>
static constexpr auto CallableWith = is_callable<T, Args...>::value;


template <typename F,
std::enable_if_t<
CallableWith<F, int>
>* = nullptr
>
int fun( F f ) // line 8
{
    return f(3);
}

int l7(int x)
{
    return x%7;
}

int main()
{
    auto l = [](int x) -> int{
        return x%7;
    };

    std::cout << "fun(l) returns " << fun(l) << std::endl;

    std::cout << CallableWith<decltype(l7), int> << std::endl;    // prints 1
    std::cout << CallableWith<decltype(l7), float> << std::endl;  // prints 1 because float converts to int
    std::cout << CallableWith<decltype(l7), const std::string&> << std::endl; // prints 0
}

【讨论】:

    【解决方案3】:

    查看std::is_invocable,它还涵盖了 C++17 中的 lambda(std::is_callable 不存在)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-08-14
      • 2019-01-07
      • 2019-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多