【问题标题】:pass multiple arguments to another function using template variadic function使用模板Variadic函数将多个参数传递给另一个函数
【发布时间】:2019-01-16 13:49:34
【问题描述】:

让我们考虑以下函数:

static void Print(const Type& type, const std::string& message, const std::string& variable) {
    Log(type, message + ": " + variable);
}

我希望它传递任意数量的变量(我的意思是 std::string & 变量 - 它包含一个变量名),然后通过 Log() 函数一起发送它们,因此,我考虑使用模板可变参数函数(重载的Print())。我会这样定义它:

template <typename Arg, typename ...Args)
static void Print(const Type& type, const std::string& message,
                  const Arg& arg, const Args&... args);

然后:

Print(type, message, args...);
Log(type, message + ": " + arg);

只是一个想法,这很可能会像这样工作:

  • args... 将被传递,Print() 函数将被递归调用,直到没有剩下的参数,
  • 但同时会调用Log()函数,基本上每次都会记录。

我需要做的是以某种方式记住arg 的值,但这需要使用附加参数调用Print(),我不太喜欢这个想法。你还有其他线索吗?

【问题讨论】:

  • 您需要一个支持函数来完成实际工作。任何日志记录前缀都附加到 Print 的日志中,然后其他所有内容都由 PrintImpl 递归处理。
  • 我在这里考虑过这种解决方案,虽然它可能不是什么漂亮的东西,但它会起作用。

标签: c++ c++11 templates variadic-templates variadic-functions


【解决方案1】:

根据所需的格式,您也许可以使用折叠表达式:

template<class... Args>
void Print(const Type& type, const std::string& message, const Args&... arg)
{
    std::stringstream strstr;
    strstr << message << ": "; // Or your prefix computation, whatever you want.

    ((strstr << arg << ", "), ...);

    std::string toLog = strstr.str();
    // Remove last separator characters.
    toLog.erase(toLog.end() - 2, toLog.end());
    Log(type, strstr.str());
}

Demo

【讨论】:

  • 我可以有一个条件,它检查 arg 的大小,然后把点放在最后。您的解决方案似乎是合理的,因为它不需要我使用某种处理所有东西的烘焙功能。我会试着玩一下。
  • 您仍然可以使用 int[] 技巧来执行 C++17 之前的临时折叠表达式
【解决方案2】:

我稍微简化了您的示例,因此假设我正确理解了您想要做什么,如果您的编译器不支持 @Max Langhof 建议的 C++17 折叠,您可以执行以下 2 个解决方案之一。

它们都适用于任何支持 operator+ 的类型来做正确的事情,但如果你的 concat 函数是别的东西,修改起来很简单。

方案一,递归解包:

template <typename Arg>
static void Print(const Arg& message, const Arg& arg1)
{
    Log(message + ": " + arg1);
}

template <typename Arg, typename... Args>
static void Print(const Arg& message, const Arg& arg1, const Arg& arg2, const Args&... variables)
{
    Print(message, arg1 + ", " + arg2, variables...);
}

选项 2,解压到 std:vector:

template <typename Arg, typename... Args>
static void Print2(const Arg& message, const Arg& arg1, const Args&... variables)
{
    std::vector<Arg> args = { variables... };
    Arg result = std::accumulate(args.begin(), args.end(), arg1, [](const Arg& a, const Arg& b) {
        return a + ", " + b;});
    Log(message + ": " + result);
}

请注意,此版本将在 std::vector 中创建参数的副本,这与其他解决方案不同。

这两个例子都可以按以下方式使用:

static void Log(const std::string& m)
{
    std::cout << m << std::endl;
}

int main()
{
    std::string msg = "MyMessage1";
    std::string var1 = "Var1";
    std::string var2 = "Var2";
    std::string var3 = "Var3";
    std::string var4 = "Var4";
    std::string var5 = "Var5";

    Print(msg, var1);
    Print(msg, var1, var2);
    Print(msg, var1, var2, var3);
    Print(msg, var1, var2, var3, var4);
    Print(msg, var1, var2, var3, var4, var5);
}

【讨论】:

  • 谢谢! @Max Langhof 很棒,但是当它发生时,我不能使用 C++17 的功能。我有一个问题,如果所有事情都这样解决,我真的需要非模板函数吗?
  • 你的意思是日志功能吗?我不知道,我只是把它作为一个占位符。假设您有一些更复杂的日志记录机制(例如,有时记录到文件,有时通过网络,有时只是到 std::cout),这就是您调用它的地方。如果您只想调用 cout,请跳过 Log 函数,只在 Print 函数的终止版本(只接受一个参数的那个)中使用 cout
  • 我的意思实际上是 Print 函数(没有模板),但你可能也给了我一个答案(如果你的意思是 Print 接受单个参数的函数是没有任何模板的函数) .
  • 你不需要它 - 带有签名的版本 static void Print(const Arg& message, const Arg& arg1) 是终止函数,你可以在那里完成所有处理。您的 'arg1' 将是带有 ', ' 分隔符的连接变量,而 msg 将是您作为 msg 传递的任何内容
【解决方案3】:

在我看来,Max Langhof 的解决方案简单而优雅。

不幸的是,它使用了仅从 C++17 开始可用的模板折叠。

我建议使用 C++11/C++14 版本,而不是模板折叠,而是使用初始化未使用数组的旧技巧

template <typename ... Args>
void Print (Type const & type, std::string const & message,
            Args const & ... arg)
 {
   using unused = int[];

   std::stringstream strstr;

   strstr << message << ": ";

   (void)unused { 0, (strstr << arg << ", ", 0)... };

    std::string toLog = strstr.str();

    // Remove last separator characters.
    toLog.erase(toLog.end() - 2, toLog.end());
    Log(type, strstr.str());
 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-14
    • 2012-08-02
    • 1970-01-01
    • 2012-09-25
    • 2020-10-03
    相关资源
    最近更新 更多