是否可以计算出 lambda 的参数类型和返回类型?

发布于 2024-12-12 19:59:42 字数 1001 浏览 7 评论 0原文

给定一个 lambda,是否可以找出它的参数类型和返回类型?如果是,怎么办?

基本上,我想要 lambda_traits 可以通过以下方式使用:

auto lambda = [](int i) { return long(i*10); };

lambda_traits<decltype(lambda)>::param_type  i; //i should be int
lambda_traits<decltype(lambda)>::return_type l; //l should be long

背后的动机是我想在接受 lambda 作为参数的函数模板中使用 lambda_traits ,并且我需要知道函数内部的参数类型和返回类型:

template<typename TLambda>
void f(TLambda lambda)
{
   typedef typename lambda_traits<TLambda>::param_type  P;
   typedef typename lambda_traits<TLambda>::return_type R;

   std::function<R(P)> fun = lambda; //I want to do this!
   //...
}

目前,我们可以假设 lambda 只接受一个参数。

最初,我尝试使用 std::function 作为:

template<typename T>
A<T> f(std::function<bool(T)> fun)
{
   return A<T>(fun);
}

f([](int){return true;}); //error

但它显然会给出错误。因此我将其更改为 TLambda 版本的函数模板,并希望在函数内部构造 std::function 对象(如上所示)。

Given a lambda, is it possible to figure out it's parameter type and return type? If yes, how?

Basically, I want lambda_traits which can be used in following ways:

auto lambda = [](int i) { return long(i*10); };

lambda_traits<decltype(lambda)>::param_type  i; //i should be int
lambda_traits<decltype(lambda)>::return_type l; //l should be long

The motivation behind is that I want to use lambda_traits in a function template which accepts a lambda as argument, and I need to know it's parameter type and return type inside the function:

template<typename TLambda>
void f(TLambda lambda)
{
   typedef typename lambda_traits<TLambda>::param_type  P;
   typedef typename lambda_traits<TLambda>::return_type R;

   std::function<R(P)> fun = lambda; //I want to do this!
   //...
}

For the time being, we can assume that the lambda takes exactly one argument.

Initially, I tried to work with std::function as:

template<typename T>
A<T> f(std::function<bool(T)> fun)
{
   return A<T>(fun);
}

f([](int){return true;}); //error

But it obviously would give error. So I changed it to TLambda version of the function template and want to construct the std::function object inside the function (as shown above).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

安稳善良 2024-12-19 19:59:43

有趣的是,我刚刚编写了一个 function_traits 实现 基于 在 lambda 上专门化模板C++0x 可以给出参数类型。正如该问题的答案中所述,技巧是使用 lambda 的 operator()decltype

template <typename T>
struct function_traits
    : public function_traits<decltype(&T::operator())>
{};
// For generic types, directly use the result of the signature of its 'operator()'

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
    enum { arity = sizeof...(Args) };
    // arity is the number of arguments.

    typedef ReturnType result_type;

    template <size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        // the i-th argument is equivalent to the i-th tuple element of a tuple
        // composed of those arguments.
    };
};

// test code below:
int main()
{
    auto lambda = [](int i) { return long(i*10); };

    typedef function_traits<decltype(lambda)> traits;

    static_assert(std::is_same<long, traits::result_type>::value, "err");
    static_assert(std::is_same<int, traits::arg<0>::type>::value, "err");

    return 0;
}

请注意,此解决方案不适用于像 [](auto x) {} 这样的通用 lambda。

Funny, I've just written a function_traits implementation based on Specializing a template on a lambda in C++0x which can give the parameter types. The trick, as described in the answer in that question, is to use the decltype of the lambda's operator().

template <typename T>
struct function_traits
    : public function_traits<decltype(&T::operator())>
{};
// For generic types, directly use the result of the signature of its 'operator()'

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
    enum { arity = sizeof...(Args) };
    // arity is the number of arguments.

    typedef ReturnType result_type;

    template <size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        // the i-th argument is equivalent to the i-th tuple element of a tuple
        // composed of those arguments.
    };
};

// test code below:
int main()
{
    auto lambda = [](int i) { return long(i*10); };

    typedef function_traits<decltype(lambda)> traits;

    static_assert(std::is_same<long, traits::result_type>::value, "err");
    static_assert(std::is_same<int, traits::arg<0>::type>::value, "err");

    return 0;
}

Note that this solution does not work for generic lambda like [](auto x) {}.

入怼 2024-12-19 19:59:43

虽然我不确定这是否严格符合标准,
ideone 编译了以下代码:

template< class > struct mem_type;

template< class C, class T > struct mem_type< T C::* > {
  typedef T type;
};

template< class T > struct lambda_func_type {
  typedef typename mem_type< decltype( &T::operator() ) >::type type;
};

int main() {
  auto l = [](int i) { return long(i); };
  typedef lambda_func_type< decltype(l) >::type T;
  static_assert( std::is_same< T, long( int )const >::value, "" );
}

但是,这仅提供了函数类型,因此结果和参数
必须从中提取类型。
如果您可以使用 boost::function_traitsresult_typearg1_type
就会达到目的。
由于 ideone 似乎不提供 C++11 模式下的提升,我无法发布
实际的代码,抱歉。

Though I'm not sure this is strictly standard conforming,
ideone compiled the following code:

template< class > struct mem_type;

template< class C, class T > struct mem_type< T C::* > {
  typedef T type;
};

template< class T > struct lambda_func_type {
  typedef typename mem_type< decltype( &T::operator() ) >::type type;
};

int main() {
  auto l = [](int i) { return long(i); };
  typedef lambda_func_type< decltype(l) >::type T;
  static_assert( std::is_same< T, long( int )const >::value, "" );
}

However, this provides only the function type, so the result and parameter
types have to be extracted from it.
If you can use boost::function_traits, result_type and arg1_type
will meet the purpose.
Since ideone seems not to provide boost in C++11 mode, I couldn't post
the actual code, sorry.

苯莒 2024-12-19 19:59:43

@KennyTMs 答案中显示的专业化方法可以扩展到涵盖所有情况,包括可变参数和可变 lambda:

template <typename T>
struct closure_traits : closure_traits<decltype(&T::operator())> {};

#define REM_CTOR(...) __VA_ARGS__
#define SPEC(cv, var, is_var)                                              \
template <typename C, typename R, typename... Args>                        \
struct closure_traits<R (C::*) (Args... REM_CTOR var) cv>                  \
{                                                                          \
    using arity = std::integral_constant<std::size_t, sizeof...(Args) >;   \
    using is_variadic = std::integral_constant<bool, is_var>;              \
    using is_const    = std::is_const<int cv>;                             \
                                                                           \
    using result_type = R;                                                 \
                                                                           \
    template <std::size_t i>                                               \
    using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; \
};

SPEC(const, (,...), 1)
SPEC(const, (), 0)
SPEC(, (,...), 1)
SPEC(, (), 0)

<强>演示

请注意,可变参数 operator() 的数量不会调整。相反,我们也可以考虑 is_variadic

The specialization method shown in @KennyTMs answer can be extended to cover all cases, including variadic and mutable lambdas:

template <typename T>
struct closure_traits : closure_traits<decltype(&T::operator())> {};

#define REM_CTOR(...) __VA_ARGS__
#define SPEC(cv, var, is_var)                                              \
template <typename C, typename R, typename... Args>                        \
struct closure_traits<R (C::*) (Args... REM_CTOR var) cv>                  \
{                                                                          \
    using arity = std::integral_constant<std::size_t, sizeof...(Args) >;   \
    using is_variadic = std::integral_constant<bool, is_var>;              \
    using is_const    = std::is_const<int cv>;                             \
                                                                           \
    using result_type = R;                                                 \
                                                                           \
    template <std::size_t i>                                               \
    using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; \
};

SPEC(const, (,...), 1)
SPEC(const, (), 0)
SPEC(, (,...), 1)
SPEC(, (), 0)

Demo.

Note that the arity is not adjusted for variadic operator()s. Instead one can also consider is_variadic.

若能看破又如何 2024-12-19 19:59:43

@KennyTMs 提供的答案效果很好,但是如果 lambda 没有参数,则使用索引 arg<0>不编译。如果其他人遇到这个问题,我有一个简单的解决方案(比使用 SFINAE 相关解决方案更简单)。

只需将 void 添加到 arg 结构中元组的末尾,位于可变参数类型之后。即,

template <size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...,void>>::type type;
    };

由于数量不依赖于模板参数的实际数量,因此实际数量不会不正确,如果它是 0,则至少 arg<0>仍然会存在,你可以用它做你想做的事。如果您已经计划不超过索引 arg 那么它不应该干扰您当前的实现。

The answer provided by @KennyTMs works great, however if a lambda has no parameters, using the index arg<0> does not compile. If anyone else was having this problem, I have a simple solution (simpler than using SFINAE related solutions, that is).

Just add void to the end of the tuple in the arg struct after the variadic argument types. i.e.

template <size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...,void>>::type type;
    };

since the arity isn't dependent on the actual number of template parameters, the actual won't be incorrect, and if it's 0 then at least arg<0> will still exist and you can do with it what you will. If you already plan to not exceed the index arg<arity-1> then it shouldn't interfere with your current implementation.

倒带 2024-12-19 19:59:43

这是我的函数特征的玩具版本。我利用了 std::function 的类型推断,而没有实际构建任何 std::function 对象。

这允许使用常规函数,而不仅仅是函子。


template <typename Return, typename... Args>
struct FunctionTraits_ {
  FunctionTraits_(std::function<Return(Args...)> f) {}
  using return_type = Return;
  using arg_types = std::tuple<Args...>;

  enum { arity = sizeof...(Args) };
  template <size_t i>
  struct arg_type {
    using type = typename std::tuple_element<i, arg_types>::type;
  };
};

template <typename Function>
struct FunctionTraits : public decltype(FunctionTraits_(
                            std::function{std::declval<Function>()})) {};

// Testing
std::size_t func(int, const std::string &s) { return s.size(); }
auto lambda = [](int, const std::string &s) { return s.size(); };

int main() {
  using traits = FunctionTraits<decltype(func)>;
  // using traits = FunctionTraits<decltype(lambda)>;

  static_assert(traits::arity == 2);
  static_assert(std::is_same_v<traits::return_type, std::size_t>);
  static_assert(std::is_same_v<traits::arg_type<0>::type, int>);
  static_assert(std::is_same_v<traits::arg_type<1>::type, const std::string &>);
  static_assert(std::is_same_v<traits::arg_types, std::tuple<int, const std::string &>>);

  return 0;
}

感谢 Cœur 的灵感。

Here is my toy version of function traits. I took advantage of std::function's type inference without actually building any std::function object.

This allows working with regular functions, not just functors.


template <typename Return, typename... Args>
struct FunctionTraits_ {
  FunctionTraits_(std::function<Return(Args...)> f) {}
  using return_type = Return;
  using arg_types = std::tuple<Args...>;

  enum { arity = sizeof...(Args) };
  template <size_t i>
  struct arg_type {
    using type = typename std::tuple_element<i, arg_types>::type;
  };
};

template <typename Function>
struct FunctionTraits : public decltype(FunctionTraits_(
                            std::function{std::declval<Function>()})) {};

// Testing
std::size_t func(int, const std::string &s) { return s.size(); }
auto lambda = [](int, const std::string &s) { return s.size(); };

int main() {
  using traits = FunctionTraits<decltype(func)>;
  // using traits = FunctionTraits<decltype(lambda)>;

  static_assert(traits::arity == 2);
  static_assert(std::is_same_v<traits::return_type, std::size_t>);
  static_assert(std::is_same_v<traits::arg_type<0>::type, int>);
  static_assert(std::is_same_v<traits::arg_type<1>::type, const std::string &>);
  static_assert(std::is_same_v<traits::arg_types, std::tuple<int, const std::string &>>);

  return 0;
}

Thanks, Cœur, for the inspiration.

梦屿孤独相伴 2024-12-19 19:59:43

如果您正在为 C++ 中可调用的所有类型寻找完整的解决方案,那么很多答案都有效,但错过了一些极端情况,例如

  • 引用
  • 对 lambda函数和函数指针的

这是据我所知的完整解决方案(通用 lambda 除外) - 如果缺少任何内容,请在评论中告诉我:

template <typename>
struct closure_traits;

template <typename FunctionT> // overloaded operator () (e.g. std::function)
struct closure_traits
    : closure_traits<decltype(&std::remove_reference_t<FunctionT>::operator())>
{
};

template <typename ReturnTypeT, typename... Args> // Free functions
struct closure_traits<ReturnTypeT(Args...)>
{
    using arguments = std::tuple<Args...>;

    static constexpr std::size_t arity = std::tuple_size<arguments>::value;

    template <std::size_t N>
    using argument_type = typename std::tuple_element<N, arguments>::type;

    using return_type = ReturnTypeT;
};

template <typename ReturnTypeT, typename... Args> // Function pointers
struct closure_traits<ReturnTypeT (*)(Args...)>
    : closure_traits<ReturnTypeT(Args...)>
{
};

// member functions
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
    : closure_traits<ReturnTypeT(Args...)>
{
    using class_type = ClassTypeT;
};

// const member functions (and lambda's operator() gets redirected here)
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...) const>
    : closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
{
};

免责声明:std::remove_reference 的灵感来自 此代码

If you're looking for a complete solution for all types in C++ that can be invoked, a lot of these answers work but miss some of the corner cases, like

  • A reference to a lambda
  • Functions and function pointers

Here's a complete solution to my knowledge (except for generic lambdas) - let me know in the comments if anything is missing:

template <typename>
struct closure_traits;

template <typename FunctionT> // overloaded operator () (e.g. std::function)
struct closure_traits
    : closure_traits<decltype(&std::remove_reference_t<FunctionT>::operator())>
{
};

template <typename ReturnTypeT, typename... Args> // Free functions
struct closure_traits<ReturnTypeT(Args...)>
{
    using arguments = std::tuple<Args...>;

    static constexpr std::size_t arity = std::tuple_size<arguments>::value;

    template <std::size_t N>
    using argument_type = typename std::tuple_element<N, arguments>::type;

    using return_type = ReturnTypeT;
};

template <typename ReturnTypeT, typename... Args> // Function pointers
struct closure_traits<ReturnTypeT (*)(Args...)>
    : closure_traits<ReturnTypeT(Args...)>
{
};

// member functions
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
    : closure_traits<ReturnTypeT(Args...)>
{
    using class_type = ClassTypeT;
};

// const member functions (and lambda's operator() gets redirected here)
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...) const>
    : closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
{
};

Disclaimer: the std::remove_reference was inspired by this code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文