基于模板参数类型调用函数

时间:2016-11-11 10:59:45

标签: 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); 
         // fooW(str);
}

如果调用者调用foo("Abc"),则此模板函数应对fooA进行编译时调用。同样,foo(L"Abc")应该对fooW进行最后的调用。

我该怎么做?我想过使用std::conditional但是无法做到。

我无法使fooAfooB重载,因为它们是C函数。

6 个答案:

答案 0 :(得分:13)

您可以将所有您的wchar_t版本放入类模板中,例如overloads及其char个对应部分,如下所示:< / p>

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;

然后你可以将它们用作:

template<typename _TChar>
void foo(const _TChar* str)
{
    overloads<is_wide_char<_TChar>>::foo(str);
}

表达SFINAE让它变得简单!

其他方法是使用Expression SFINAE,这不需要您编写overloads之类的内容,并使用较少的代码执行相同的工作:

template<typename _TChar>
void foo(const _TChar* str)
{
    invokeOne(fooA, fooW, str);
}

然后您可以将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...);
 }

查看online demo

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

希望有所帮助。

答案 1 :(得分:6)

然后重载另一个函数。我假设foo做了更多工作,需要成为模板。然后调用foo_forward_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);
}

在C ++ 1z中,您将能够使用constexpr if,但说实话,我认为重载的解决方案仍然更具可读性。

template<typename _TChar>
void foo(const _TChar* str)
{
    if constexpr(std::is_same<_TChar, char>::value) {
        FooA(str);
    } else {
        FooW(str);
    }
}

或者,您可以使用Boost.Hana&#39; s overload

template<typename _TChar>
void foo(const _TChar* str)
{
    hana::overload(fooA, fooW)(str);
}

demo

顺便说一下:你应该避免在程序中使用下划线 - 大写字母名称。它们被保留用于任何用途(即宏)的实现,并可能导致令人讨厌的名称冲突。

答案 2 :(得分:6)

这对模板来说似乎很奇怪。我建议使用正常的重载:

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);
}

答案 3 :(得分:5)

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

overload_t<...>...中获取一组callables并生成一个使用标准重载决策的对象,通过继承operator()来选择它们之间:

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 );

并且str将根据通常的重载决策规则分派。这在其他地方很有用,这就是为什么值得写,而使用点的代码是自我记录。

Live example(哇,第一次写错了!)

可以为上述overload_t添加一些改进。

  • 在构造期间和辅助函数中完美转发f

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

  • 反思传入Fs;如果它们overload_t平衡了组合树。

  • 在C ++ 17中,一个func<auto>模板,它接受一个函数指针并返回一个无状态调用它的函数对象。编译器相对擅长于删除函数指针,但是当没有可能改变它们的运行时状态时,它们会更好。

  • 决定为overload_t<>做些什么。目前无法编译;也许它应该只是一个空的struct {},或者甚至是一个具有不可召唤的operator()的结构。

  • 检查现有的库,例如boost::hana::overload,看看有什么不同。

  • 通过static tag_t<F> which_overload_helper( Args... ) const方法和template<class...Args> using which_overload = typename decltype( which_overload_helper( std::declval<Args>()... ) )::type;

  • 公开提取将调用哪些重载的功能
  • 当某些传入的Fs执行/没有const operator()时,正确选择重载。函数指针应该const上是volatileoperator()还是&&?全部4个? & vs get_cart_contents_weight()重载怎么样?

答案 4 :(得分:4)

也许如果有更多目标函数或重载并不是您尝试解决的唯一问题,那么使用模板来完成此操作可能是值得的。

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

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

答案 5 :(得分:3)

您有几个选择。

  1. 使用明确专门的帮助器struct

    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);
    }
    
  2. 使用“静态if”实现(例如boost::hana::eval_ifmy own):

    template <typename _TChar>
    void foo(const _TChar* str)
    {
        vrm::core::static_if(std::is_same<_TChar, char>{})
            .then([](const auto* x_str){ FooA(x_str); })
            .else_([](const auto* x_str){ FooW(x_str); })(str);
    }
    
  3. 使用帮助程序重载函数:

    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);
    }