使用std :: ostream打印可变参数包的最简单方法是什么?

gex*_*ide 25 c++ templates variadic-templates c++11

使用逗号分隔的参数包的最简单方法是什么std::ostream

例:

template<typename... Args>
void doPrint(std::ostream& out, Args... args){
   out << args...; // WRONG! What to write here?
}

// Usage:
int main(){
   doPrint(std::cout,34,"bla",15); // Should print: 34,bla,15
}
Run Code Online (Sandbox Code Playgroud)

注意:可以假设<<操作员的相应过载可用于所有类型的参数包.

Pio*_*cki 39

没有你想要的递归调用和逗号.

/通过参数包扩展:

template <typename Arg, typename... Args>
void doPrint(std::ostream& out, Arg&& arg, Args&&... args)
{
    out << std::forward<Arg>(arg);
    using expander = int[];
    (void)expander{0, (void(out << ',' << std::forward<Args>(args)), 0)...};
}
Run Code Online (Sandbox Code Playgroud)

DEMO


使用折叠表达式:

template <typename Arg, typename... Args>
void doPrint(std::ostream& out, Arg&& arg, Args&&... args)
{
    out << std::forward<Arg>(arg);
    ((out << ',' << std::forward<Args>(args)), ...);
}
Run Code Online (Sandbox Code Playgroud)

演示2

  • @GermánDiagohttp://en.cppreference.com/w/cpp/language/parameter_pack给出了一个类似的例子:`int dummy [sizeof ...(Ts)] = {(std :: cout << args,0). ..};` (5认同)
  • 我的C++必须过时了.我根本无法解析. (5认同)
  • 作为旁注,你可以跳过`using`声明,只写`(void)(int []){(std :: cout <<','<< std :: forward <Args>(args)), 0)...};`.你也不需要括号中的第一个零或内部的"void"强制转换. (2认同)

M.M*_*M.M 18

在C++ 17中,将有一种更简单的方法(正如Kerrek SB在评论中所暗示的那样;这实际上存在于N4606中,这是第一个C++之后的草案14),称为折叠表达式:

代码是:

(out << ... << args);
Run Code Online (Sandbox Code Playgroud)

并且图案被称为二进制左折叠,其定义是等同于... .expression op ... op parameter-pack((( expression op arg1) op arg2) op arg3)op argN

我认为外部括号对于像这样的表达式语句并不是绝对必要的,但是如果fold表达式是另一个运算符的操作数,则它们是必需的,或者是一个非常好的主意:)

  • 使用分隔符:`((out &lt;&lt; ", " &lt;&lt; args), ...);` (5认同)

Ker*_* SB 8

通常的答案是定义两个单独的重载,对于基本情况使用空的重载:

// base case
void doPrint(std::ostream& out) {}

template <typename T, typename... Args>
void doPrint(std::ostream& out, T t, Args... args)
{
    out << t;                // add comma here, see below
    doPrint(out, args...);
}
Run Code Online (Sandbox Code Playgroud)

当然在实际代码中,我不会每次都复制参数,而是使用转发引用,但是你明白了.

如果你想在每个项目之后添加逗号,即使在最后一个项目之后,也只需要替换out << tout << t << ','.

如果您只想在内部使用逗号,而不是在最后一个元素之后,则需要一个单独的单参数重载,它不会打印逗号,而泛型重载在包之前需要两个不同的参数,即:

template <typename T>
void doPrint(std::ostream& out, T t)
{
    out << t;
}

template <typename T, typename U, typename... Args>
void doPrint(std::ostream& out, T t, U u, Args... args)
{
    out << t << ',';
    doPrint(out, u, args...);
}
Run Code Online (Sandbox Code Playgroud)


Max*_*kin 6

参数包扩展仅适用于普通函数调用,而不适用于中缀运算符.因此,您需要将s << x语法转换为普通函数调用语法f(s, x):

template<class Head>
void print_args_(std::ostream& s, Head&& head) {
    s << std::forward<Head>(head);
}

template<class Head, class... Tail>
void print_args_(std::ostream& s, Head&& head, Tail&&... tail) {
    s << std::forward<Head>(head);
    print_args_(s, std::forward<Tail>(tail)...);
}

template<class... Args>
void print_args(Args&&... args) {
    print_args_(std::cout, std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)