函数打印任意数量的参数

C++ Function to print any number of arguments

本文关键字:参数 任意数 打印 函数      更新时间:2023-10-16

我遇到了下面这个用c++编写的函数。使用任意数量的参数调用函数trace(),将以

格式在参数名称旁边打印每个参数的值。

name1: value1 | name2: value2,依此类推

我想了解这段代码是如何工作的,以及像双符号&&, __VA_ARGS__这样的语法是什么意思。谢谢!

#define tr(...) trace(#__VA_ARGS__, __VA_ARGS__)
template <typename Arg1>
void trace(const char* name, Arg1&& arg1){
    cout << name << " : " << arg1 << endl;
}
template <typename Arg1, typename... Args>
void trace(const char* names, Arg1&& arg1, Args&&... args){
    const char* comma = strchr(names + 1, ',');
    cout.write(names, comma-names) << " : " << arg1 << " | " ; 
    trace(comma+1, args...);
}
#define tr(...) trace(#__VA_ARGS__, __VA_ARGS__)

是一个接受可变数量参数的函数宏。
它转发一个字符串化的版本作为第一个参数。
如果不使用预处理器,就无法派生字符串表示。

示例求值:

int main(){
    int i;
    float f;
    std::string s;
    tr(i,f,s);
}

解析为:

int main(){
    int i;
    float f;
    std::string s;
    trace("i,f,s", i,f,s);
}

两个可变的模板函数展开每个实参并递归地调用自己。

结束递归的基case函数为:

template <typename Arg1>
void trace(const char* name, Arg1&& arg1){
    cout << name << " : " << arg1 << endl;
}

这可以用折叠表达式更简洁地完成。

,,用于允许完全转发。

这个函数取决于几个机制:

  • 可变模板:为了支持任意数量的参数,函数被定义为可变模板。这个特性具有递归语法,就像您在函数式语言中遇到的那样:template <typename Arg1, typename... Args>,其中Arg1是第一个参数,Args是所有其他参数。因此函数本身的递归定义—为了解压缩形参,它调用自己作为实参列表的尾部部分。注意comma-names技巧计算参数名的长度。
  • 预处理字符串化操作符: __VA_ARGS__当然只是tr(...)参数列表的宏(逗号分隔)。在它之前添加#是创建字符串的原因,它作为trace(const char* names, ...)的第一个参数传递。
  • 引用崩溃 (Arg&&在模板中):这实际上是这里最复杂的东西,这很遗憾,因为它几乎什么都不做。您可以直接删除&&s,代码仍然可以像预期的那样工作。什么,,它调整预期参数的引用类型以匹配实际传递的内容,例如,如果你通过引用传递一个参数,它不会复制它,如果你传递一个临时对象,它将使用move语义。这个特性的实现技术说起来有点痛苦,所以这里有另一个so问题需要进一步阅读