如何在c++中为一个接受模板化参数并在其上应用模板化函数的函数设置模板

How do I template a function that takes templated args and applies a templated function on them in c++?

本文关键字:函数 参数 应用 设置 c++ 一个      更新时间:2023-10-16

我有一堆静态类函数,它们接受不同数量的{string,int,float}参数和一个Output参数。基于调用的函数,对于相同的参数可能存在不同的行为。例如:

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

我希望有一种简单、安全的方法来编写模板,以便对每个函数执行类似的行为,基本上是使用Output参数进行调用,并将其转换为要返回的字符串。理想情况下,无需再次指定参数类型。它可能看起来像这样:

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,也不确定如何别名或定义静态函数。有一些不太优雅的解决方案,但它们需要重复的样板,我想避免。

使用类,您可以执行以下操作:

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

演示

如果将Output参数移到前面,就可以执行此操作。

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>

因为CCD_ 1是一个值,而CCD_。

有可能有这样的

OutputString<decltype(ChangeOutput1), ChangeOutput1>

然后用宏消除重复,但这太难看了。

我选择在运行时而不是在编译时传递函数,这样更容易。