有两个“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
但是无法做到。
我无法使fooA
或fooB
重载,因为它们是C函数。
答案 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);
}
其他方法是使用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);
}
顺便说一下:你应该避免在程序中使用下划线 - 大写字母名称。它们被保留用于任何用途(即宏)的实现,并可能导致令人讨厌的名称冲突。
答案 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
上是volatile
,operator()
还是&&
?全部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)
您有几个选择。
使用明确专门的帮助器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);
}
使用“静态if”实现(例如boost::hana::eval_if或my 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);
}
使用帮助程序重载函数:
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);
}