如何模板化一个接受模板化参数并在 C++ 中对其应用模板化函数的函数?
Posted
技术标签:
【中文标题】如何模板化一个接受模板化参数并在 C++ 中对其应用模板化函数的函数?【英文标题】:How do I template a function that takes templated args and applies a templated function on them in c++? 【发布时间】:2015-11-16 05:25:53 【问题描述】:我有一堆静态类函数,它们接受不同数量的 string, int, float 参数和一个输出参数。根据调用的函数,相同的参数可能会有不同的行为。例如:
static void ChangeOutput1(const string& foo, int bar, Output* output);
static void ChangeOutput2(int bar, Output* output);
static void ChangeOutput3(float foo, Output* output);
static void ChangeOutput4(float foo, Output* output); // behaves differently from ChangeOutput3
我希望有一种简单、安全的方式来编写模板以对每个函数执行类似的行为,基本上是使用输出参数调用并将其转换为要返回的字符串。理想情况下无需再次指定参数类型。它可能看起来像这样:
template<typename... Args, int (*ChangeOutputFn)(Args...)>
string OutputString(Args... args)
Output output;
ChangeOutputFn(args..., &output);
return ConvertToString(output);
// Is there a way to alias static templated functions?
using StringOutput1 = OutputString<ChangeOutput1>;
using StringOutput2 = OutputString<ChangeOutput2>;
using StringOutput3 = OutputString<ChangeOutput3>;
我不确定如何实现这一点。我不确定如何编写 OutputString 以及如何别名或定义静态函数。有一些不太优雅的解决方案,但它们需要我想避免的重复样板。
【问题讨论】:
只要给它们都一样的名字(即重载,不要模板)。 Für C++11,看看可变参数模板。但是,我没有检查它是否可以完美满足您的所有要求 @n.m. - 我不能把它们都命名为相同的东西;对于不同的情况,相同的签名可能具有不同的语义(例如,有多种情况只采用一个 int,但对输出执行不同的操作)。 【参考方案1】:有了一个类,你可以这样做:
template <typename T, T f> struct OutputString;
template<typename... Args, void (*ChangeOutputFn)(Args...)>
struct OutputString<void (*)(Args...), ChangeOutputFn>
template <typename ... Ts>
auto operator()(Ts... args)
-> decltype(ChangeOutputFn(std::forward<Ts>(args)..., std::declval<Output *>()),
std::string)
Output output;
ChangeOutputFn(std::forward<Ts>(args)..., &output);
return ConvertToString(output);
;
然后
using StringOutput1 = OutputString<decltype(&ChangeOutput1), &ChangeOutput1>;
using StringOutput2 = OutputString<decltype(&ChangeOutput2), &ChangeOutput2>;
using StringOutput3 = OutputString<decltype(&ChangeOutput3), &ChangeOutput3>;
并将其用作
std::string s2 = StringOutput2(42);
std::string s3 = StringOutput3(4.2f);
Demo
【讨论】:
疯狂的模板魔法,但大概一开始问题就变得不必要地复杂了。为什么不简单地重载相同的函数,类似于std::to_string()
?
@Jarod42 使用 c++11 编译此代码会给我错误“'int(int, struct Output*)' is not a valid type for a template non-type parameter using StringOutput1 = OutputString;"我错过了什么?
@Walter 它不能被重载,因为 ChangeOutput3 和 ChangeOutput4 具有相同的签名
@Jarod42 好的,发现了。 decltype 不执行指向函数衰减的指针,因此“使用”行必须是 decltype(&ChangeOutput1)...
@LoPiTaL:添加了演示,并修复了编译。【参考方案2】:
如果将输出参数移到前面,则可以这样做。
static void ChangeOutput1(Output*, const std::string& foo, int bar);
static void ChangeOutput2(Output*, int bar);
static void ChangeOutput3(Output*, float foo);
static void ChangeOutput4(Output*, float foo);
现在你可以拥有这个模板了:
template<typename... Args>
std::function<std::string(Args...)>
mkOutput (void (*ChangeOutputFn)(Output*, Args...))
return [ChangeOutputFn](Args... args)->std::string
Output output;
ChangeOutputFn(&output, args...);
return ConvertToString(output);
;
和“函数别名”看起来像这样:
auto a1 = mkOutput(ChangeOutput1);
auto a2 = mkOutput(ChangeOutput2);
auto a3 = mkOutput(ChangeOutput3);
auto a4 = mkOutput(ChangeOutput4);
注意 1. 你不能拥有这种语法
OutputString<ChangeOutput1>
因为ChangeOutput1
是一个值,OutputString
必须事先知道它的类型,或者将其作为另一个模板参数接收。
可能有这样的东西
OutputString<decltype(ChangeOutput1), ChangeOutput1>
然后用宏消除重复,但这很丑。
我选择在运行时而不是在编译时传递函数,这样更容易。
【讨论】:
以上是关于如何模板化一个接受模板化参数并在 C++ 中对其应用模板化函数的函数?的主要内容,如果未能解决你的问题,请参考以下文章
C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译
C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译