可变递归模板

jwm*_*jwm 5 c++ templates variadic-templates c++11 c++14

我有一个特殊的格式,需要使用空格分隔的标记和最终的空终止符(null是输出的一部分).我创建了一个函数来将一系列以空格分隔的标记发送到输出流:

// C++ variadic template function to output tokens to a stream delimited by spaces
template <typename T>
void join(std::ostream& os, T const& arg)
{
  // This is the last argument, so insert a null in the stream to delimit
  os << arg << '\000';
}

// Join one, add a space, try again.
template <typename T, typename... Args>
void join(std::ostream& os, T const& arg, Args... args)  // recursive variadic function
{
  os << arg << " ";
  join(os, args...);
}
Run Code Online (Sandbox Code Playgroud)

这对于像这样的事情很好join(os, 1, foo, "THING", M_PI);.但是,我也有一些需要不以空格分隔的标记,比如join(os, "KEY=", val);.

我试着玩这些参数,想想也许我可以填充nullptr参数列表并使用它来重载方法,跳过空间,但我不能为我的生活弄清楚如何做这种超载.

在标准合规性普遍存在之前,我所看到的关于可变参数模板的大多数问题都相当陈旧.如果在其他地方已经回答了这个问题,请指出.我正在使用GCC 7.3.

或者,告诉我,我过于复杂应该非常简单.也许可变参数模板不是这个螺栓的正确锤子?

Lin*_*gxi 5

介绍一个帮手

template <typename A, typename B>
struct pack_t {
  const A& a;
  const B& b;
};

template <typename A, typename B>
std::ostream& operator<<(std::ostream& os, pack_t<A, B> pac) {
  return os << pac.a << pac.b; 
}

template <typename A, typename B>
auto pack(const A& a, const B& b) noexcept {
  return pack_t<A, B>{a, b}; 
}
Run Code Online (Sandbox Code Playgroud)

像它一样使用它

join(std::cout, "Hello", "World", pack("pi=", 3.14));
Run Code Online (Sandbox Code Playgroud)

完整代码(现场)

#include <iostream>

template <typename A, typename B>
struct pack_t {
  const A& a;
  const B& b;
};

template <typename A, typename B>
std::ostream& operator<<(std::ostream& os, pack_t<A, B> pac) {
  return os << pac.a << pac.b; 
}

template <typename A, typename B>
auto pack(const A& a, const B& b) noexcept {
  return pack_t<A, B>{a, b}; 
}

template <typename T>
void join(std::ostream& os, const T& arg) {
  os << arg << '\0';
}

template <typename T, typename... Args>
void join(std::ostream& os, const T& arg, const Args&... args) {
  os << arg << ' ';
  join(os, args...);
}

int main() {
  join(std::cout, "Hello", "World", pack("pi=", 3.14));
}
Run Code Online (Sandbox Code Playgroud)

请注意,您可以延长助手通过立足以支持更比两个参数pack_tstd::tuple通过聚集或继承.例如,

namespace impl {

template <typename T, std::size_t... Idx>
struct pack_t {
  T v;
};

template <std::size_t... Idx, typename... Ts>
auto pack(std::index_sequence<Idx...>, Ts&&... vs) noexcept {
  auto v = std::forward_as_tuple(std::forward<Ts>(vs)...);
  return pack_t<decltype(v), Idx...>{std::move(v)};
}

template <typename T, std::size_t... Idx>
std::ostream& operator<<(std::ostream& os, pack_t<T, Idx...> args) {
  return ((os << std::get<Idx>(std::move(args.v))), ...);
}

}

template <typename... Ts>
auto pack(Ts&&... vs) noexcept {
  return impl::pack(
    std::index_sequence_for<Ts...>{}, std::forward<Ts>(vs)...);
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以打包不同数量的参数(实时).