代码之家  ›  专栏  ›  技术社区  ›  spraff

我是否可以基于其运算符()的签名专门化变量模板参数

  •  1
  • spraff  · 技术社区  · 6 年前

    假设我有这样一个函数

    template <typename... FunctionList>
    void call_all (int i, float f, const FunctionList... function_list);
    
    template <>
    void call_all (int, float)
    {
    }
    

    我想把它专门化如下:

    template <typename HasIntArgument, typename... FL>
    void call_all (int i, float f, const HasIntArgument & hia, const FL... list)
    {
        hia (i);
        call_all (i, f, list...);
    }
    
    template <typename HasFloatArgument, typename... FL>
    void call_all (int i, float f, const HasFloatArgument & hfa, const FL... list)
    {
        hfa (f);
        call_all (i, f, list...);
    }
    

    换句话说,我希望这个函数,对于 function_list ,确定是否可以用签名调用它 void(int) void(float) . (此列表中的任何内容都不能使用多个签名进行调用。)

    我希望它能与原始函数指针、lambdas或任何与 operator() .

    我可以直接写一个合适的专业,或者我必须用traits类和SFINAE做一些奇怪的事情吗?

    2 回复  |  直到 6 年前
        1
  •  1
  •   Jarod42    6 年前

    你可以这样做:

    #if 0 // C++17
    template <typename F>
    void dispatch(F func, int i, float f)
    {
        if constexpr (has_int_argument<F>::value) {
            func(i);
        } else {
            func(f);
        }
    }
    #else // C++11
    template <typename F>
    typename std::enable_if<has_int_argument<F>::value>::type
    dispatch(F func, int i, float)
    {
        func(i);
    }
    template <typename F>
    typename std::enable_if<!has_int_argument<F>::value>::type
    dispatch(F func, int, float f)
    {
        func(f);
    }
    #endif    
    
    
    template <typename... Fs>
    void call_all (int i, float f, const Fs&... fs)
    {
        // (dispatch(fs, i, f), ...); // C++17
    
        const int dummy[] = {0, (dispatch(fs, i, f), 0)...};
        static_cast<void>(dummy);
    }
    

    具有适当的功能特性 has_int_argument . 类似于:

    template <typename ClassOrSig> struct funct_trait;
    
    template <typename C>
    struct funct_trait : funct_trait<decltype(&C::operator())> {};
    
    template <typename C, typename Ret, typename ...Args>
    struct funct_trait<Ret (C::*) (Args...)> : funct_trait<Ret(Args...)> {};
    
    template <typename C, typename Ret, typename ...Args>
    struct funct_trait<Ret (C::*) (Args...) const> : funct_trait<Ret(Args...)> {};
    
    // &&, &, volatile, ... (C ellipsis)
    
    template <typename Ret, typename ...Args>
    struct funct_trait<Ret (*)(Args...)> : funct_trait<Ret(Args...)> {};
    
    template <typename Ret, typename ...Args>
    struct funct_trait<Ret (Args...)>
    {
        using sig_type = Ret(Args...);
        using args_tuple = std::tuple<Args...>;
        // ...
    };
    
    template <typename T>
    using has_int_argument = std::is_same<std::tuple<int>,
                                          typename funct_trait<T>::args_tuple>;
    

    Demo

        2
  •  0
  •   Yakk - Adam Nevraumont    6 年前
    template<class...Fs>struct overloaded:Fs...{
      using Fs::operator()...;
    };
    template<class...Fs>
    overloaded(Fs...)->overloaded<Fs...>;
    

    上面的有点复杂 但是实现在整个地方都存在。

    namespace details {
      struct secret_tag {};
      struct secret_result {
        template<class...Ts>
        secret_tag operator()(Ts&&...) const;
      };
      template<class F>
      using secret_tester = overloaded<std::decay_t<F>, secret_result>;
    }      
    template<class F, class Arg>
    using match_arg_exactly = std::integral_constant<
      bool,
      !std::is_same<
        details::secret_tag, 
        std::result_of_t< details::secret_tester<F>(Arg) >
      >{}
    >;
    

    现在我们可以要求给定的对象是否能精确匹配特定的参数。

    template <typename HasIntArgument>
    void call_one(int i, float f, std::true_type, const HasIntArgument & hia)
    {
      hia (i);
    }
    template <typename HasFloatArgument>
    void call_one(int i, float f, std::false_type, const HasFloatArgument& hia)
    {
      hia (f);
    }
    template <typename F>
    void call_one(int i, float f, const F & hia)
    {
      call_one( i, f, match_arg_exactly<const F&, int>{}, hia );
    }
    

    我们用这个:

    void call_all (int, float)
    {}
    
    template<class F, class...Fs>
    void call_all (int i, float f, F const& f0, Fs const&...fs) {
      call_one( i, f, f0 );
      call_all(i, f, fs...); 
    }
    

    测试代码:

    struct float_eater {
      void operator()(float x)const{ std::cout<< "float "<<x<<"\n"; }
    };
    struct int_eater {
      void operator()(int x)const{ std::cout<< "int "<<x<<"\n"; }
    };
    
    call_all( 42, 3.14, float_eater{}, int_eater{}, int_eater{} );
    

    Live example

    一个 overloaded 有点像:

    template<class...Fs>
    struct overloaded;
    
    template<class F0>
    struct overloaded<F0>:F0 {
      overloaded(F0 f0):F0(std::move(f0)) {}
      using F0::operator();
    };
    
    template<class F0, class F1>
    struct overloaded<F0, F1>: F0, F1 {
      overloaded( F0 f0, F1 f1 ):F0(std::move(f0)), F1(std::move(f1)) {}
      using F0::operator();
      using F1::operator();
    };
    
    template<class F0, class...Fs>
    struct overloaded<F0, Fs...>:
      overloaded<F0, overloaded<Fs...>>
    {
      overloaded(F0 f0, Fs...fs):
        F0(std::move(f0)),
        overloaded<Fs...>( std::move(fs)... )
      {}
    };
    

    我认为这对我们的目的是足够的。(更一般地说,你要么建立一个二叉树,平衡或不平衡),并处理完美的转发,和。。。等。