C++ 基于模板参数类型的调用函数

C++ 基于模板参数类型的调用函数,c++,c++11,visual-c++,c++14,C++,C++11,Visual C++,C++14,有两个“C”功能: void fooA(const char*); void fooW(const wchar_t*); 还有一个包装器模板函数: template<typename _TChar> void foo(const _TChar* str) { // call fooA or fooB based on actual type of _TChar // std::conditional .. ? // fooA(str);

有两个“C”功能:

void fooA(const char*);
void fooW(const wchar_t*);
还有一个包装器模板函数:

template<typename _TChar>
void foo(const _TChar* str)
{
     // call fooA or fooB based on actual type of _TChar
     // std::conditional .. ?
         // fooA(str); 
         // fooW(str);
}
void helper(const char* x) { FooA(x); }
void helper(const wchar_t* x) { FooW(x); }

template <typename _TChar>
void foo(const _TChar* str)
{
    helper(str);
}
模板
void foo(const_TChar*str)
{
//根据实际的\u TChar类型调用fooA或fooB
//std::条件?
//fooA(str);
//fooW(str);
}
如果调用方调用
foo(“Abc”)
,则此模板函数应在编译时调用
fooA
。类似地,
foo(L“Abc”)
应该最后调用
fooW

我该怎么做?我曾想过使用std::conditional,但没有成功


我不能使
fooA
fooB
重载,因为它们是C函数。

您可以将所有您的
wchar\t
版本放在类模板中,比如
重载
及其
char
计数器部分专用化,如下所示:

template<typename WideCharVersion> 
struct overloads
{
    void foo(wchar_t const * arg)
    {
       FooW(arg);
    }
    //more wchar_t functions
};

template<> 
struct overloads<std::false_type>
{
    void foo(char const * arg)
    {
       FooA(arg);
    }
    //more char functions
};

//a friendly alias!
template<typename T>
using is_wide_char = typename std::is_same<whar_t, T>::type;
然后您可以实现
invokeOne
,如下所示:

 template<typename F1, typename F2, typename ... Args>
 auto invokeOne(F1 f1, F2 f2, Args && ... args) -> decltype(f1(args...))
 {
     return f1(args...);
 }

 template<typename F1, typename F2, typename ... Args>
 auto invokeOne(F1 f1, F2 f2, Args && ... args) -> decltype(f2(args...))
 {
     return f2(args...);
 }
模板
自动调用一个(F1、F2、参数和…参数)->decltype(F1(参数…)
{
返回f1(args…);
}
模板
自动调用One(F1、F2、F2、Args&…Args)->decltype(F2(Args…)
{
返回f2(args…);
}
看一看这张照片

在这种方法中,您不必将重载添加到
重载
类模板中,也不必将重载添加到其专门化中。相反,您只需将它们作为参数传递给
invokeOne
,后者为您调用正确的重载


希望有帮助。

然后重载另一个函数。我假设
foo
做了更多的工作,需要成为一个模板。然后调用
foo\u forward\u call
,定义如下:

void foo_forward_call(char const* ptr) {
    FooA(ptr);
}

void foo_forward_call(wchar_t const* ptr) {
    FooW(ptr);
}
在呼叫站点:

template<typename _TChar>
void foo(const _TChar* str)
{
    foo_forward_call(str);
}

或者,您可以使用
Boost.Hana

模板
void foo(const_TChar*str)
{
hana::过载(fooA,fooW)(str);
}



顺便说一下:您应该避免在程序中使用下划线大写字母名称。它们保留用于实现任何用途(即宏),可能会导致严重的名称冲突。

您有一些选择

  • 使用显式专用的辅助对象
    结构

    template <typename>
    struct helper;
    
    template<>
    struct helper<char>
    {
        void operator()(const char* x){ FooA(x); }
    };
    
    template<>
    struct helper<wchar_t>
    {
        void operator()(const wchar_t* x){ FooW(x); }
    };
    
    template <typename _TChar>
    void foo(const _TChar* str)
    {
        helper<_TChar>{}(str);
    }
    
    模板
    结构助手;
    模板
    结构辅助程序
    {
    void运算符()(const char*x){FooA(x);}
    };
    模板
    结构辅助程序
    {
    void运算符()(常量wchar_t*x){FooW(x);}
    };
    模板
    void foo(const_TChar*str)
    {
    助手{}(str);
    }
    
  • 使用“静态if”实现(例如或):

    模板
    void foo(const_TChar*str)
    {
    vrm::core::static_if(std::is_same{})
    。然后([](const auto*x_str){FooA(x_str);})
    .else(const auto*x_str){FooW(x_str);})(str);
    }
    
  • 使用帮助器重载函数:

    template<typename _TChar>
    void foo(const _TChar* str)
    {
         // call fooA or fooB based on actual type of _TChar
         // std::conditional .. ?
             // fooA(str); 
             // fooW(str);
    }
    
    void helper(const char* x) { FooA(x); }
    void helper(const wchar_t* x) { FooW(x); }
    
    template <typename _TChar>
    void foo(const _TChar* str)
    {
        helper(str);
    }
    
    void helper(const char*x){FooA(x);}
    void helper(const wchar_t*x){FooW(x);}
    模板
    void foo(const_TChar*str)
    {
    助手(str);
    }
    

  • 对于模板来说,这似乎是一件非常奇怪的事情。我建议改用普通重载:

    void foo(const char* p) { fooA(p); }
    void foo(const wchar_t* p) { fooW(p); }
    
    如果您坚持使用模板,则可以这样做:

    template <typename T>
    void foo(const T* p)
    {
        // Declare functions here so that calling fooW with const char*
        // and 'calling' fooA with const wchar_t* would not cause compile error.
        void fooA(const T*);
        void fooW(const T*);
    
        if (std::is_same<char, T>::value)
            fooA(p);
        else
            fooW(p);
    }
    
    模板
    无效foo(常数T*p)
    {
    //在此声明函数,以便使用const char调用fooW*
    //使用const wchar\u t*调用fooA不会导致编译错误。
    无效fooA(常数T*);
    void fooW(const T*);
    if(std::is_same::value)
    食品添加剂(p);
    其他的
    fooW(p);
    }
    
    如果有更多的目标函数,或者重载不是您试图解决的唯一问题,那么使用模板来完成这项工作可能是值得的

    但在这种情况下,只需编写您的重载:

    void foo(const char* x) { fooA(x); }
    void foo(const wchar_t* x) { fooW(x); }
    

    我一般喜欢解决问题。因此,让我们设计一种机制来过载

    重载\u t
    通过继承
    操作符()
    ,在
    ..
    中获取一组可调用项,并生成一个使用标准重载分辨率在它们之间进行选择的对象:

    根据通常的重载解决规则,
    str
    将被分派到其中一个。这在其他地方很有用,这就是为什么它值得编写的原因,并且使用的代码是自文档化的

    (哇,第一次写对了!)

    有许多改进可以添加到上面的
    重载\u t

    • 在构建过程中和助手函数中完美地转发
      f
      s

    • 平衡的二叉树继承而不是线性继承(重要的是要完成多个重载)。这可能会影响运行时和编译时性能,特别是对于大量函数

    • 内省传入的
      Fs
      ;如果它们过载,则平衡组合树

    • 在C++17中,一种
      func
      模板,它接受函数指针并返回一个无状态调用它的函数对象。编译器在省略函数指针方面相对较好,但在没有可能改变函数指针的运行时状态时,编译器在这方面做得更好

    • 决定如何处理
      过载\u t
      。目前没有编译,;也许它应该是一个空的
      结构{}
      ,或者甚至是一个带有不可调用的
      操作符()的结构

    • 检查现有的库,比如
      boost::hana::重载
      ,看看有什么不同

    • 公开提取哪些重载将被调用的能力,可能通过
      静态标记\u t which\u-overload\u-helper(Args…)const
      方法和
      使用which\u-overload=typename decltype的模板(which\u-overload\u-helper(std::declval())::type

    • 当一些传入的
      Fs
      s没有/没有
      const
      操作符()
      时,正确选择重载。函数指针是否在
      操作符()上同时具有
      常量
      易失性
      ?全部4个?
      &&void foo(const char* p) { fooA(p); }
      void foo(const wchar_t* p) { fooW(p); }
      
      template <typename T>
      void foo(const T* p)
      {
          // Declare functions here so that calling fooW with const char*
          // and 'calling' fooA with const wchar_t* would not cause compile error.
          void fooA(const T*);
          void fooW(const T*);
      
          if (std::is_same<char, T>::value)
              fooA(p);
          else
              fooW(p);
      }
      
      void foo(const char* x) { fooA(x); }
      void foo(const wchar_t* x) { fooW(x); }
      
      template<class...Fs>
      struct overload_t;
      // the case where we have a function object:
      template<class F>
      struct overload_t<F>:F{
        overload_t(F f):F(std::move(f)){}
        using F::operator();
        // boilerplate to ensure these are enabled if possible:
        overload_t(overload_t&&)=default;
        overload_t(overload_t const&)=default;
        overload_t& operator=(overload_t&&)=default;
        overload_t& operator=(overload_t const&)=default;
      };
      // we cannot inherit from a function pointer.  So
      // store one, and write an `operator()` that forwards to it:
      template<class R, class...Args>
      struct overload_t<R(*)(Args...)>{
        using F=R(*)(Args...);
        F f;
        overload_t(F fin):f(fin){}
        R operator()(Args...args)const{
          return f(std::forward<Args>(args)...);
        }
        overload_t(overload_t&&)=default;
        overload_t(overload_t const&)=default;
        overload_t& operator=(overload_t&&)=default;
        overload_t& operator=(overload_t const&)=default;
      };
      // the case where we have more than type to overload.
      // recursively inherit from the one-arg and the rest-of-arg
      // and using operator() to bring both of their () into equal standing:
      template<class F0, class...Fs>
      struct overload_t<F0,Fs...>:
        overload_t<F0>,
        overload_t<Fs...>
      {
        using overload_t<F0>::operator();
        using overload_t<Fs...>::operator();
        overload_t(F0 f0, Fs...fs):
          overload_t<F0>(std::move(f0)),
          overload_t<Fs...>(std::move(fs)...)
        {}
        overload_t(overload_t&&)=default;
        overload_t(overload_t const&)=default;
        overload_t& operator=(overload_t&&)=default;
        overload_t& operator=(overload_t const&)=default;
      };
      // a helper function to create an overload set without
      // having to specify types.  Will be obsolete in C++17:
      template<class...Fs>
      overload_t<Fs...> overload(Fs...fs){ return {std::move(fs)...};}
      
      overload(FooA,FooW)( str );