接受字符串和整数的可变参数函数,格式化后者并连接所有?

A variadic function that accepts Strings and Ints, Format the latter and concatenate all?

我正在尝试在这个 question 中使用 DanielKO 的答案来满足我的需要,但我不熟悉模板和可变参数函数,我不知道我该怎么做。

我需要的是一个可变的 c++(11) 函数,我可以这样调用它:

 String NewMsg = CreateMessage("SET",16,1,17,0,"RED",47);

并有 NewMsg= "SET,0010,0001,0011,0000,RED,002F".

我什至不知道应该在参数之间添加逗号的位置。 接着: 我如何在解析 args 时区分整数和字符串,以便将每个整数格式化为十六进制字符串?

你使用递归和函数重载

std::string CreateMessage(int i)
{
    return /* i formatted as hex */;
}

std::string CreateMessage(const char* s)
{
    return s;
}

template<typename T, typename... Ts>
std::string CreateMessage(T t, Ts... ts)
{
    return CreateMessage(t) + "," + CreateMessage(ts...);
}

一种选择是对模板使用递归,正如 Passer By 在他的回答中所做的那样。但是,在我看来,一个更优雅的解决方案(如果您能够使用 C++17 语言功能)是使用 fold expression 来避免递归。表达式被扩展,以便直接为每个参数调用 Append,有点像在编译时评估的参数之间的 for 循环。

template <class T>
void Append(std::ostringstream &out, T &&arg) {
    out << "," << std::forward<T>(arg);
}

template <class... TArgs>
std::string CreateMessage(TArgs &&...args) {
    std::ostringstream out;
    (Append(out, std::forward<TArgs>(args)), ...);
    return out.str().substr(1);
}

现场演示 here

混合其他两种解决方案(来自 Passer By 的递归解决方案和来自 Nick Mertin 的 C++17 折叠表达式),您也可以编写 CreateMessage() 而无需递归(以类似的折叠表达式方式)在 C++11 中

std::string const & getStr (std::string const & ret)

 { return ret; }

std::string getStr (int val)
 {
   std::ostringstream ret;

   ret << std::hex << std::setw(4) << std::setfill('0') << val;

   return ret.str();
 }

template <typename ... Ts>
std::string CreateMessage (Ts const & ... args)
 {
   using unused = int[];

   std::string ret   = "";
   std::string comma = "";

   (void)unused { 0, ( ret += comma + getStr(args), comma = ",", 0 )... };

   return ret;
 }

-- 编辑 --

OP 询问

Would you like to teach me how that "simil-fold" line works? How should i "read" it?

嗯...下面是一行

(void)unused { 0, ( ret += comma + getStr(args), comma = ",", 0 )... };

其中 unusedint[].

的别名 (using)

它利用了可变参数包(模板或函数参数)可以在 C 风格数组初始化的上下文中扩展的事实,还利用了逗号运算符的强大功能([= 的逗号运算符的专有性) 74=] 并丢弃逗号左侧的内容)。

所以你有

的扩展(对于 args... 中的每个参数)
( ret += comma + getStr(args), comma = ",", 0 )

哪里

1) 您将 comma + getStr(args) 添加到 ret,其中 comma 对于第一个参数为空,对于以下参数等于 ","(请参阅 (2))

2) 第一个逗号运算符丢弃 ret 的值并将 "," 分配给 comma (所以你在第一个 [=26= 中有一个空的 comma ] 和 "," 以下 ret +=

3) 第二个逗号运算符丢弃逗号和return的值0到unuses

的初始化

因此 ret 递增,所有 getStr(args)"," 分隔,未使用的数组用零初始化。

观察另外两点:

a) 在数组(未命名的 unused)初始化列表中,您有一个起始且与可变参数无关的零 ({ 0,);这是必要的,以防 args... 列表为空,因此该行变为 (void)unsed { 0 };,这是合法的,而不是 (void)unused { };(没有那个零),这是一个语法错误

b) unused前面是(void);这不是绝对必要的,但有助于避免 "object defined but not used".

类型的警告