【问题标题】:Enable template if functor passed as argument takes no argument如果作为参数传递的函子没有参数,则启用模板
【发布时间】:2019-08-14 15:33:22
【问题描述】:

我正在尝试为模板函数定义两个重载。 如果作为参数传递的函子没有参数,则生成第一个重载,否则生成第二个重载。 我开始这样实现:

template <typename R, typename... Types>
constexpr size_t argumentCount(R (*f)(Types...))
{
    return sizeof...(Types);
}

template <typename Function>
typename std::enable_if<argumentCount<Function>() == 0, int>::value = 0 > callFunc(Function fu)
{
    fu();
}

template <typename Function, typename... Params>
typename std::enable_if<argumentCount<Function>() == 0, int>::value = 0 > callFunc(Function fu, Params... params)
{
    fu(std::forward<decltype(params)>(params)...);
}

这不编译有几个原因,包括解析错误。我想做的是,

callFunc([](){}); 将调用重载 1,而callFunc([](int value1,int value2){}); 将调用重载 2。

我怎样才能做到这一点?

【问题讨论】:

  • FWIW 您的最后一个函数将适用于这两种情况。拥有template &lt;typename Function, typename... Params&gt; void callFunc(Function fu, Params... params) { fu(std::forward&lt;decltype(params)&gt;(params)...); } 可以让你做到callFunc([](){}); 和callFunc([](int value1,int value2){}, 1, 2);

标签: c++ templates overloading variadic-templates sfinae


【解决方案1】:

您可以使用带有std::void_t 和std::declval 的检测习语来检测此类特征。

template <typename FuncT>
using invocable_without_args_t = std::void_t<decltype(std::declval<FuncT>()())>;

更多信息是available here;这篇文章帮助我了解了它的工作原理。

【讨论】:

    【解决方案2】:

    如果您可以添加间接级别...使用标签调度代替 SFINAE 怎么样?

    我的意思是,如下所示

    #include <iostream>
    #include <type_traits>
    
    template <typename F>
    void callFunc_helper (F fu, std::true_type)
     {
       std::cout << "zero version" << std::endl;
    
       fu();
     }
    
    template <typename F, typename... Prs>
    void callFunc_helper (F fu, std::false_type, Prs && ... params)
     {
       std::cout << "non zero version" << std::endl;
    
       fu(std::forward<Prs>(params)...);
     }
    
    template <typename F, typename... Prs>
    void callFunc (F fu, Prs && ... params)
     { callFunc_helper(fu, std::integral_constant<bool, 0u == sizeof...(Prs)>{},
                       std::forward<Prs>(params)...); }
    
    int main ()
     {
       callFunc([]{});
       callFunc([](int, int){}, 0, 1);
     }
    

    如果你真的想检查函数参数的数量而不是以下参数的数量,显然你可以使用std::integral_constant&lt;bool, 0u == argumentCount&lt;Function&gt;()&gt;{}(但为什么?)。

    【讨论】:

    • 显然,在我尝试过的其他想法中,这非常接近解决方案。但这会检查传递的参数数量,而不是函数接受的参数数量。
    • 考虑,Qt Signal Slot 实现。其中信号传递参数,而插槽不必接受所有参数。
    • @MuhammetAliAsan - 如答案末尾所述,如果您想通过功能接受的参数数量选择正确的功能,可以使用std::integral_constant&lt;bool, 0u == argumentCount&lt;Function&gt;()&gt;{}而不是std::integral_constant&lt;bool, 0u == sizeof...(Prs)&gt;{}
    【解决方案3】:
    1. 这是您使用的固定语法版本:
    template <typename R, typename... Types>
    constexpr size_t argumentCount(R (*f)(Types...))
    {
        return sizeof...(Types);
    }
    
    template <typename Function>
    typename std::enable_if<argumentCount<Function>() == 0, int> callFunc(Function fu)
    {
        fu();
    }
    
    template <typename Function, typename... Params>
    typename std::enable_if<argumentCount<Function>() == 0, int> callFunc(Function fu, Params... params)
    {
        fu(std::forward<decltype(params)>(params)...);
    }
    
    1. 在此特定示例中,您不需要额外的重载,Types... 可以为空。
        template <typename Function, typename... Params>
        auto callFunc(Function fu, Params... params)
        {
            fu(std::forward<decltype(params)>(params)...);
        }
    

    效果很好。

    【讨论】:

      【解决方案4】:

      如果你需要两个模板函数,就写吧:

      #include <iostream>
      
      template <class R>
      void callFunc(R (*f)()) {
          std::cout << "Called no-arg template\n";
          f();
      }
      
      template <class R, class T, class... Types, class... Params>
      void callFunc(R (*f)(T, Types...), Params... params) {
          std::cout << "Called multi-arg template\n";
          f(params...);
      }
      
      void g() {
          std::cout << "Called g\n";
      }
      
      void h(int) {
          std::cout << "Called h\n";
      }
      
      int main() {
          callFunc(g);
          callFunc(h, 3);
          return 0;
      }
      

      输出:

      [temp]$ clang++ -std=c++11 test.cpp
      [temp]$ ./a.out
      Called no-arg template
      Called g
      Called multi-arg template
      Called h
      [temp]$ 
      

      【讨论】:

        猜你喜欢
        • 2010-11-13
        • 2023-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-14
        • 1970-01-01
        相关资源
        最近更新 更多