【问题标题】:How do I template a function that takes templated args and applies a templated function on them in c++?如何模板化一个接受模板化参数并在 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,但对输出执行不同的操作)。

标签: c++ templates function-pointers alias member-function-pointers


【解决方案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>

然后用宏消除重复,但这很丑。

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

【讨论】:

    猜你喜欢
    • 2010-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-23
    • 1970-01-01
    • 1970-01-01
    • 2022-11-30
    相关资源
    最近更新 更多