如何在一个可在外面访问的struct中创建一个可变参数模板?

问题描述 投票:1回答:2

我正在使用code from another answer来获取lambda函数的类型(返回和参数)。以下是答案中的相关代码:

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.
    };
};

我想创建一个参数列表来自动将lambda转换为函数指针:

template<typename Func>
constexpr auto l2f(Func lambda) {
    typedef function_traits<Func> traits;
    return static_cast<traits::result_type(*)( ..all the args.. )(lambda);
}

现在我正在做的是,我在function_traits结构中添加了一个函数:

template <typename Func>
static auto convertToFunctionPointer(Func fn) {
    return static_cast<ReturnType(*)(Args...)>(fn);
}

它有效,但我真正想知道的是如何在function_traits中提供Args的内容以从外部访问它,以及如何“内联”多个模板然后展开它们。像这样的东西(不起作用):

return static_cast<traits::result_type(*)(traits::arg<std::make_index_sequence<traits::arity>...>::type...)(lambda);
c++ templates c++14 metaprogramming variadic-templates
2个回答
0
投票

但我真正想知道的是如何在function_traits中提供Args的内容以从外部访问它,以及如何“内联”多个模板然后展开它们。

首先,我想到的最简单的解决方案是用l2f()来解决你的特殊问题,它定义了一个特定的类型,如PasserBy建议的signature

我提出了一个稍微不同的解决方案,但想法是一样的:你可以定义

using func_type = std::function<ReturnType(Args...)>;

所以l2f()简直成了

template <typename Func>
constexpr auto l2f (Func lambda)
 { return static_cast<typename function_traits<Func>::func_type>(lambda); }

对于更一般的问题(“如何在function_traits中提供Args的内容以从外部访问它,以及如何”内联“多个模板然后展开它们”)我建议避免定义模板arg结构但是只需在function_traits中定义一个元组类型

using tuple_type  = std::tuple<Args...>;

接下来你必须调用辅助函数来改变arity中的function_traits中的std::index_sequence

template <typename Func>
constexpr auto l2f (Func lambda)
 { return l2f_helper(lambda,
      std::make_index_sequence<function_traits<Func>::arity>{});  }

并且辅助函数可以写成

template <typename Func, std::size_t ... Is>
constexpr auto l2f_helper (Func lambda,
                           std::index_sequence<Is...> const &)
 {
   return static_cast<std::function<
      typename function_traits<Func>::result_type(
         std::tuple_element_t<Is,
            typename function_traits<Func>::tuple_type> ...)>>(lambda);
 }

丑陋。我知道。

以下是完整的可编译示例,其中包含l2f和修改过的function_traits两个版本。

#include <tuple>
#include <iostream>
#include <functional>
#include <type_traits>

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

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
 {
   static constexpr std::size_t arity { sizeof...(Args) };

   using result_type = ReturnType;
   using func_type   = std::function<ReturnType(Args...)>;
   using tuple_type  = std::tuple<Args...>;
 };

template <typename Func>
constexpr auto l2f_v1 (Func lambda)
 { return static_cast<typename function_traits<Func>::func_type>(lambda); }

template <typename Func, std::size_t ... Is>
constexpr auto l2f_v2_helper (Func lambda,
                              std::index_sequence<Is...> const &)
 {
   return static_cast<std::function<
      typename function_traits<Func>::result_type(
         std::tuple_element_t<Is,
            typename function_traits<Func>::tuple_type> ...)>>(lambda);
 }

template <typename Func>
constexpr auto l2f_v2 (Func lambda)
 { return l2f_v2_helper(lambda,
      std::make_index_sequence<function_traits<Func>::arity>{});  }

int main ()
 {
   auto f1 = l2f_v1([](int, long){ std::cout << "lambda1!" << std::endl; });
   auto f2 = l2f_v2([](int, long){ std::cout << "lambda2!" << std::endl; });

   f1(1, 2L);
   f2(2, 3L);

   static_assert( std::is_same<decltype(f1), decltype(f2)>{}, "!" );
 }

一点改进:如果你还在using中定义了以下function_traits模板

template <std::size_t I>
using a_type = std::tuple_element_t<I, tuple_type>;

辅助函数可以简化如下

template <typename Func, std::size_t ... Is>
constexpr auto l2f_helper (Func lambda,
                              std::index_sequence<Is...> const &)
 {
   return static_cast<std::function<
      typename function_traits<Func>::result_type(
         typename function_traits<Func>::template a_type<Is>...)>>(lambda);
 }

1
投票

作为Nawaz pointed out,并非所有lambdas都可以转换为指针,只有那些没有捕获的指针。对于那些没有捕获的人,他们有一个隐含的转换,可以通过在前面添加一个+来强制转换。

但要直接回答您的问题,只需在特征中使用别名

template<typename C, typename R, typename... Args>
struct function_traits<R (C::*)(Args...)>
{
    using signature = R(Args...);
};

template<typename F>
auto cast(F f)
{
    return static_cast<typename function_traits<F>::signature*>(f);
}
© www.soinside.com 2019 - 2024. All rights reserved.