使用c ++中的可变参数模板迭代参数包

时间:2014-10-24 14:33:07

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

作为日志库的一部分,我希望能够迭代参数包,将每个值写入流。然而,我的第一次尝试并没有编译。第一个错误是"错误C2144:语法错误:' int'之前应该有'}'"。

#include <sstream>
#include <ostream>
#include <iomanip>
#include <fstream>

template <typename ...Args>
std::ostream & Write(std::ostream & o, std::initializer_list<Args...> list) 
{
    size_t size = list.size();

    if(list.size() > 0)
    {
        for(size_t i = 0; i < (size - 1); i++)
            o << list[i] << ", ";

        o << list[i];
    }

    return o;
}

template<typename ...Args>
std::ostream & Write(std::ostream & o, Args...)
{
    return Write(o, { Args... });
}

int main(int argc, wchar_t * argv[])
{
    std::ostringstream o;

    Write(o, 1, "Hello", 2, "World", 3, 1.4857);

    // o should contain the string of characters "1, Hello, 2, World, 3, 1.4857"

    return 0;
}

如何迭代...中的每个项目并将其发送到流?

4 个答案:

答案 0 :(得分:7)

递归是一种选择:

template<typename Arg>
std::ostream & Write(std::ostream & o, Arg&& arg) { 
    return o << std::forward<Arg>(arg); 
}

template<typename Arg, typename ...Args>
std::ostream & Write(std::ostream & o, Arg&& arg, Args&&... args)
{
    o << std::forward<Arg>(arg) << ", ";
    return Write(o, std::forward<Args>(args)...);
}

Demo

或者,包扩展技巧仍然有效,稍微调整一下 - 你需要特殊情况下列表中的第一项:

template<typename Arg, typename ...Args>
std::ostream & Write(std::ostream & o, Arg&& arg, Args&&... args)
{
    o << std::forward<Arg>(arg);

    using expander = int[];
    (void) expander{ (o << ", " << std::forward<Args>(args), void(), 0)... };

    return o;
}

Demo

答案 1 :(得分:2)

如果最后一个额外的逗号可以,请使用

template<typename... Args>
std::ostream& Write(std::ostream& o, Args&&... args)
{
    std::initializer_list<bool> { o << std::forward<Args>(args) << ", "... };
    return o;
}

否则你需要递归:

template <typename A>
std::ostream& Write(std::ostream& o, A&& a)
{  return o << std::forward<A>(a);  }

template <typename A, typename... Args>
std::ostream& Write(std::ostream& o, A&& a, Args&&... args)
{
    return Write(o << std::forward<A>(a) << ", ", std::forward<Args>(args)...);
}

答案 2 :(得分:2)

这是我喜欢使用的实用程序:

#define VARIADIC_DETAIL_CAT2(a, b) a ## b
#define VARIADIC_DETAIL_CAT(a, b) VARIADIC_DETAIL_CAT2(a, b)

#define VARIADIC_EXPAND(...) \
    int VARIADIC_DETAIL_CAT(libutil_expando, __COUNTER__) [] = { 0, \
      ((__VA_ARGS__), 0)... \
    } \
    /**/

有了这个,你可以写:

template<typename... Args>
std::ostream & Write(std::ostream& o, Args&&... args)
{
    VARIADIC_EXPAND(o << std::forward<Args>(args));
}

获得正确的分隔符非常棘手。

答案 3 :(得分:0)

可以编写一个辅助函数来遍历参数包:

// Invoke each of the functions `f` in sequence
template<typename... F>
void invoke_all(F&&... f) {
    std::initializer_list<bool>{(f(), false)...};
}

根据您的情况,可以用作:

template<typename... Args>
std::ostream& Write(std::ostream& os, Args&&... args) {
    auto joiner = std::experimental::ostream_joiner(os, ", ");
    invoke_all([&]() {
        *joiner++ = std::forward<Args>(args);
    }...);
}