基于模板参数类型调用函数
Call function based on template argument type
有两个"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函数。
您可以将所有版本的wchar_t
放在一个类模板中,例如overloads
和它们的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;
然后你可以使用它们作为:
template<typename _TChar>
void foo(const _TChar* str)
{
overloads<is_wide_char<_TChar>>::foo(str);
}
表达式SFINAE使它很容易!
另一种方法是使用表达式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...);
}
看一下在线演示。
在这种方法中,您不必将重载添加到overloads
类模板中,也不必将添加到其专门化中。相反,您只需将它们作为参数传递给invokeOne
, CC_14将为您调用正确的重载。
希望对你有帮助。
然后重载另一个函数。我假设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
的overload
:
template<typename _TChar>
void foo(const _TChar* str)
{
hana::overload(fooA, fooW)(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);
}
总的来说我喜欢解决问题。因此,让我们设计一种机制来重载内容。
overload_t<...>
接受...
中的一组可调用对象,并通过继承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
将根据通常的过载解析规则被调度到其中一个。这在其他地方很有用,这就是为什么它值得编写的原因,并且代码在使用时是自文档化的。
实例(哇,第一次就写对了!)
对于上述overload_t
,可以添加许多改进。
f
s在构建过程和helper函数中的完美转发。平衡二叉树继承而不是线性继承(重要的是完成了多个过载)。这可能会对运行时和编译时的性能产生影响,特别是对于大量的函数。
反省传入
Fs
;在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
s没有const
operator()
时,正确地选择过载。函数指针应该在operator()
上有const
,volatile
,两者都有还是都没有?所有4 ?&&
和&
的过载情况如何?
如果有更多的目标函数或者重载不是你想要解决的唯一问题,那么使用模板来解决这个问题可能是值得的。
在这种情况下,只需写重载:
void foo(const char* x) { fooA(x); }
void foo(const wchar_t* x) { fooW(x); }
你有几个选择。
-
使用显式专门化的帮助器
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或我自己的):
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); }
-
使用helper重载函数:
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); }
我认为使用typeid()
是非常优雅和可读的,但不确定是否有任何副作用。
#include <typeinfo>
template <typename T>
void foo(){
if(typeid(T) == typeid(const char*)){
fooA();
}
else if (typeid(T) == typeid(const wchar_t*)){
fooB();
}
}
在wandbox中查看
- 如何用参数值调用函数(仅在运行时已知)
- 从python中调用C++函数并获取返回值
- 当使用通配符和null指针调用函数时,对输出的说明
- 从R调用C++函数并对其进行集成时出错
- 使用QTreeView,如何通过调用函数只突出显示特定的行/列
- 如何在qt中从另一个类调用函数
- 在 COUT 语句中使用 COUT 调用函数
- 如何从线程中的不同模块调用函数?
- C++从函数指针数组调用函数
- 当 A 在 for 循环中调用函数 B 时,如何计算函数 A 的空间复杂度?
- 如何在 C/C++ 中从外部库调用函数
- 如何使用运算符在同一行中多次调用函数
- 是否可以创建一个从不同类调用函数的线程?
- 无法为类成员调用函数
- 如何从另一个标头 c++ 调用函数
- C++有什么方法可以在既不调用函数模板也不提供其模板参数的情况下引用函数模板?
- 如何只允许在调用函数 B 后调用函数 A?
- 我可以这样调用函数吗?
- 如何在 c++ 的类中递归调用函数方法?
- 为什么在指向对象的迭代器上调用函数不允许我更改对象本身?