【问题标题】:How to deduce second parameter type from first parameter?如何从第一个参数推断出第二个参数类型?
【发布时间】:2018-09-08 03:16:45
【问题描述】:

我是模板元编程的新手。第二个参数将与传递的函数参数相同。我想从Func推导出第二个参数类型。

template<typename Func>
void execute(Func func, decltype(Func) t) 
{
    std::cout << func(t) << std::endl;
}

int main() 
{
    std::function<int(float)> func1 = [](float f) { return int(f); };
    execute(func1,1.5);
    return 0;
}

这可行,但我不想声明额外的typenameme T,因为信息已经在Func 中可用,所以为什么不推断。

template<typename Func, typename T>
void execute(Func func, T t) 
{
    std::cout << func(t) << std::endl;
}

【问题讨论】:

  • 看看可变参数模板cplusplus.com/articles/EhvU7k9E
  • 你想只接受 std::function 还是任何可调用对象?你想接受一个只有一个参数的函数吗?
  • @Eugene: execute 应该接受一个 std::function 和第二个参数作为参数
  • 你为什么不想要额外的模板参数?
  • @Eugene:类型信息已经存在于 Func 本身中,我想从 Func 中减少。

标签: c++ c++11 templates lambda c++17


【解决方案1】:

我想从Func推导出第二个参数类型

我不想声明额外的typename T

除了在binding with the argument 之后将可调用对象传递给函数之外,我没有看到任何简单的解决方案来满足您的要求。

以下将确保您的第二个要求并且不需要更改您的原始代码。

#include <functional>
#include <iostream>

template<typename Func>
void execute(Func func) {
    std::cout << func() << std::endl;
}
int main() 
{
    auto func1 = std::bind([](float f) { return int(f); }, 2.5f);
    execute(func1);
    return 0;
}

【讨论】:

    【解决方案2】:

    在您的示例中,不需要知道确切的参数类型,因此这里最简单的解决方案是使用可变参数模板。只需获取一个参数包并转发它们:

    template<typename Func, typename... Args>
    void execute(Func func, Args&&... a) {
        std::cout << func(std::forward<Args>(a)...) << std::endl;
    }
    
    int main() {
        auto func1 = [](float f) { return int { f }; };
        execute(func1, 1.5);
        auto func2 = [](int i) { return float { i }; };
        execute(func2, 15);
        auto func3 = [](int a, int b, int c) { return a * b + c; };
        execute(func3, 3, 4, 5);
        return 0;
    }
    

    【讨论】:

    • 我不想声明额外的typename T 这不符合 OP 的要求。
    • 我读到了。从给出的示例中,并不明显为什么该要求是必要的。这只是解决问题的最简单方法。
    • 我认为是因为:我想从Func中推导出第二个参数类型。但是,OP 没有提到具体的用例。
    【解决方案3】:

    这是一个例子:

    #include <tuple>
    #include <type_traits>
    
    template <typename> struct function_traits;
    
    template <typename Function>
    struct function_traits
        : public function_traits<decltype(
              &std::remove_reference<Function>::type::operator())> {};
    
    template <typename ClassType, typename ReturnType, typename... Arguments>
    struct function_traits<ReturnType (ClassType::*)(Arguments...) const>
        : function_traits<ReturnType (*)(Arguments...)> {};
    
    /* support the non-const operator ()
     * this will work with user defined functors */
    template <typename ClassType, typename ReturnType, typename... Arguments>
    struct function_traits<ReturnType (ClassType::*)(Arguments...)>
        : function_traits<ReturnType (*)(Arguments...)> {};
    
    template <typename ReturnType, typename... Arguments>
    struct function_traits<ReturnType (*)(Arguments...)> {
      typedef ReturnType result_type;
    
      using argument_tuple = std::tuple<Arguments...>;
      template <std::size_t Index>
      using argument = typename std::tuple_element<Index, argument_tuple>::type;
    
      static const std::size_t arity = sizeof...(Arguments);
    };
    
    template <typename Function, std::size_t Index>
    using nth_argument_type =
        typename function_traits<Function>::template argument<Index>;
    
    #include <iostream>
    using namespace std;
    
    template <typename FN>
    void execute(FN func, nth_argument_type<FN,0> arg0) {
      std::cout << func(arg0) << std::endl;
    };
    
    int main() {
      int i = 7;
      auto fn = [](int a) { return a * a; };
      auto fn2 = [](int a) mutable { return a * a; };
      execute(fn, 5);
      execute(fn, i);
      execute(fn2, 5);
      execute(fn2, i);
    };
    

    【讨论】:

      【解决方案4】:

      您应该这样做。如果这样做,您的示例将停止工作,因为您的函数是 int(float),但参数是 double

      #include <functional>
      #include <iostream>
      
      template<typename Arg>
      void execute(std::function<int(Arg)> func, Arg t) {
          std::cout << func(t) << std::endl;
      }
      
      int main() {
          std::function<int(float)> func1 = [](float f) { return int(f); };
          execute(func1,1.5);
          return 0;
      }
      

      这不会编译。 Live demo.

      保持T参数,完全OK。

      【讨论】:

        【解决方案5】:

        您正在寻找的是一种类型特征,它提供对函数签名中使用的类型的访问。标准库中不包含这样的类型特征。因此,您要么需要使用实现某个库的库,要么自己实现它。

        如果您可以使用 boost,它们的类型特征中就完全实现了这一点:https://www.boost.org/doc/libs/1_68_0/libs/type_traits/doc/html/boost_typetraits/reference/function_traits.html 您可能希望使用 argN_type 成员来获取第 N 个参数的类型。

        如果您不能使用 boost,则需要实现自己的类型特征以使参数类型可用。它可能看起来像这样:

        // Base case for non-function types
        template<typename T>
        struct func_types { };
        
        // Case for any generic function signature
        template<typename Return, typename ...Args>
        struct func_types<Return(Args...)>
        {
            using ReturnType = Return;
            using ArgsTuple = std::tuple<Args...>;
        
            template<std::size_t N>
            struct args
            {
                using Type = std::tuple_element_t<N, ArgsTuple>;
            };
        };
        
        // Specialization for function pointers
        template<typename Return, typename ...Args>
        struct func_types<std::function<Return(Args...)>> : public func_types<Return(Args...)> { };
        
        // Specialization for std::function
        template<typename Return, typename ...Args>
        struct func_types<std::function<Return(Args...)>> : public func_types<Return(Args...)> { };
        
        
        // All further specializations for member functions,
        // lambdas, etc. are left as an exercise to the reader
        
        template<typename Func>
        void execute(Func func, typename func_types<Func>::template args<0>::Type t)
        {
            std::cout << func(t) << std::endl;
        }
        

        This example is basically a stripped down version of this blog post。这使用了 C++14 中添加的功能,因此您需要针对该版本或更高版本进行编译。

        【讨论】:

        • 类似@ark1974?
        猜你喜欢
        • 1970-01-01
        • 2019-01-22
        • 2020-09-24
        • 2019-11-25
        • 2021-12-20
        • 2021-11-11
        • 1970-01-01
        • 1970-01-01
        • 2011-10-11
        相关资源
        最近更新 更多