我正试图在question中使用DanielKO的答案满足我的需要,但是我对模板和可变参数函数不熟悉,并且我不知道该怎么做。
我需要一个可变的c ++(11)函数,我可以这样调用它:
String NewMsg = CreateMessage("SET",16,1,17,0,"RED",47);
并具有NewMsg =“ SET,0010,0001,0011,0000,RED,002F”。
我什至无法在参数之间添加逗号。 接着: 解析args时,如何区分整数和字符串,以便将每个整数格式化为十六进制字符串?
答案 0 :(得分:8)
您使用递归和函数重载
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...);
}
答案 1 :(得分:3)
一种选择是对模板使用递归,就像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。
答案 2 :(得分:2)
混合其他两种解决方案(来自Passer By的递归解决方案和来自Nick Mertin的C ++ 17折叠表达式),您也可以在C中编写CreateMessage()
,而无需进行递归(以类似折叠表达方式) ++ 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询问
您想教我“类似折叠”字样的工作方式吗?我应该如何“阅读”它?
嗯...下面是这行
(void)unused { 0, ( ret += comma + getStr(args), comma = ",", 0 )... };
其中unused
是using
的别名(int[]
)。
它使用了可变参数包(模板或函数自变量)可以在C样式数组初始化的上下文中进行扩展的事实,并且还使用了逗号运算符的功能(exec / compute和丢弃逗号左侧的内容。
因此(对于args...
中的每个参数,您都有
( ret += comma + getStr(args), comma = ",", 0 )
其中
1)在comma + getStr(args)
上添加ret
,其中第一个参数comma
为空,随后的参数等于","
(请参阅(2))>
2)第一个逗号运算符丢弃ret
的值,并将","
分配给comma
(因此,前comma
中有一个空白ret +=
, ","
用于关注ret +=
3)第二个逗号运算符舍弃逗号的值,并将0返回到unuses
的初始化
因此ret
递增,而所有getStr(args)
被","
隔开,未使用的数组用零初始化。
还要注意几点:
a)在数组(未命名的unused
)初始化列表中,您有一个起始且与变参无关的零({ 0,
);如果args...
列表为空,则此行是必需的,因此该行变为(void)unsed { 0 };
,这是合法的,而不是(void)unused { };
(不带零),这是一个语法错误
b)unused
之前是(void)
;这不是严格必要的,但是对于避免发出“对象已定义但未使用”类型的警告很有用。