使用模板可变参数

时间:2018-08-09 07:31:39

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

让我们考虑以下功能:

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(),我真的不喜欢这个想法。您还有其他线索吗?

3 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分: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());
 }

答案 2 :(得分:1)

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

它们两者都可以在支持operator +的任何类型上工作,以完成正确的操作,但是如果您的concat函数还有其他功能,则很容易修改。

选项1,递归拆包:

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