【问题标题】:How to Deduce Argument List from Function Pointer?如何从函数指针推导出参数列表?
【发布时间】:2017-10-02 21:04:25
【问题描述】:

给定两个或更多示例函数,是否可以编写模板化代码来推断作为模板参数提供的函数的参数?

这是一个鼓舞人心的例子:

void do_something(int value, double amount) {
    std::cout << (value * amount) << std::endl;
}

void do_something_else(std::string const& first, double & second, int third) {
    for(char c : first) 
        if(third / c == 0) 
            second += 13.7;
}

template<void(*Func)(/*???*/)>
struct wrapper {
    using Args = /*???*/;
    void operator()(Args&& ... args) const {
        Func(std::forward<Args>(args)...);
    }
};

int main() {
    wrapper<do_something> obj; //Should be able to deduce Args to be [int, double]
    obj(5, 17.4); //Would call do_something(5, 17.4);
    wrapper<do_something_else> obj2; //Should be able to deduce Args to be [std::string const&, double&, int]
    double value = 5;
    obj2("Hello there!", value, 70); //Would call do_something_else("Hello there!", value, 70);
}

/*???*/ 的两种用法中,我都在尝试找出我可以放在那里以启用这种代码的内容。

以下似乎不起作用,因为Args 在第一次使用之前没有被定义(除了我必须假设还有许多语法错误),即使它确实如此,我仍在寻找对于不需要显式编写类型本身的版本:

template<void(*Func)(Args ...), typename ... Args)
struct wrapper {
    void operator()(Args ...args) const {
        Func(std::forward<Args>(args)...);
    }
};

wrapper<do_something, int, double> obj;

【问题讨论】:

  • 你的意思是std::function
  • @bolov 我正在寻找的具体方法是从提供的Function 中推断出Argsstd::function 通过在类型参数列表中明确指定这些类型来匹配函数指针。
  • 你接受 C++17 吗?眨眼眨眼
  • @bolov 我正在使用 MSVC,所以我使用 C++17 的能力受到微软编译器实现质量的限制,但如果存在专门的 C++17 解决方案,我会很高兴看到它。
  • 请问当您可以直接致电do_somethingdo_something_else 时,拥有这样的包装器有什么好处?将指向函数的指针存储为模板参数似乎没有用。此外,如果您不想编写类型,可以利用decltype 将第一个模板参数声明为指向方法的指针,然后将指针值作为第二个参数传递。

标签: c++ templates template-meta-programming


【解决方案1】:

使用 C++17,我们可以拥有自动模板非类型参数,这使得 Wrapper&lt;do_something&gt; w{} 语法成为可能 1).

至于推断Args...,您可以使用specialization

template <auto* F>
struct Wrapper {};

template <class Ret, class... Args, auto (*F)(Args...) -> Ret>
struct Wrapper<F>
{
    auto operator()(Args... args) const
    {
        return F(args...);
    }
};
Wrapper<do_something> w{};
w(10, 11.11);

1) 如果没有 C++17,就不可能拥有 Wrapper&lt;do_something&gt; w{} 好的语法。

你能做的最好的就是:

template <class F, F* func>
struct Wrapper {};

template <class Ret, class... Args, auto (*F)(Args...) -> Ret>
struct Wrapper<Ret (Args...), F>
{
    auto operator()(Args... args) const
    {
        return F(args...);
    }
};
Wrapper<declype(do_something), do_something> w{};

【讨论】:

  • @Xirema C++17 处于实施的早期阶段。 clang 可以毫无问题地编译它:godbolt.org/g/DyArxa
【解决方案2】:

使用 C++17,您可以这样做:

template <auto FUNC, typename = decltype(FUNC)>
struct wrapper;

template <auto FUNC, typename RETURN, typename ...ARGS>
struct wrapper<FUNC, RETURN (*)(ARGS...)> {
    RETURN operator()(ARGS ...args) {
        return FUNC(args...);
    }
};

我从 W.F. 的 answer 那里学到了这项技术

【讨论】:

  • Bolov 修复了他的解决方案,实际上我现在更喜欢这个解决方案了。
【解决方案3】:

C++17版本进一步改进:更少的模板参数和适当的noexcept注解:

template<auto VFnPtr> struct
wrapper;

template<typename TResult, typename... TArgs, TResult ( * VFnPtr)(TArgs...)> struct
wrapper<VFnPtr>
{
    TResult
    operator ()(TArgs... args) const noexcept(noexcept((*VFnPtr)(::std::forward<TArgs>(args)...)))
    {
        return (*VFnPtr)(::std::forward<TArgs>(args)...);
    }
};

【讨论】:

    【解决方案4】:

    使用 C++11,您可以考虑使用模板化的 make_wrapper 辅助函数。但是,通过这种方法,函数指针不是模板参数。相反,函数指针由名为f_ 的非静态数据成员“携带”,在以下示例中:

    #include <iostream>
    
    void do_something(int value, double amount) {
      std::cout << (value * amount) << std::endl;
    }
    
    void do_something_else(std::string const& first, double & second, int third) {
      for(char c : first)
        if(third / c == 0)
          second += 13.7;
    }
    
    template<class Ret, class... Args>
    using function_pointer = Ret(*)(Args...);
    
    template<class Ret, class... Args>
    struct wrapper {
      using F = function_pointer<Ret, Args...>;
    
      F f_;
    
      explicit constexpr wrapper(F f) noexcept : f_{f} {}
    
      template<class... PreciseArgs>// not sure if this is required
      Ret operator()(PreciseArgs&&... precise_args) const {
        return f_(std::forward<PreciseArgs>(precise_args)...);
      }
    };
    
    template<class Ret, class... Args>
    constexpr auto make_wrapper(
      function_pointer<Ret, Args...> f
    ) -> wrapper<Ret, Args...> {
      return wrapper<Ret, Args...>(f);
    }
    
    int main() {
      constexpr auto obj = make_wrapper(do_something);
      obj(5, 17.4);
      constexpr auto obj2 = make_wrapper(do_something_else);
      double value = 5;
      obj2("Hello there!", value, 70);
    
      return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-30
      • 1970-01-01
      • 2018-07-27
      相关资源
      最近更新 更多