[英]Variadic Recursive Template
我有一个特殊的格式,需要使用空格分隔的标记和最终的空终止符(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...);
}
这适用于join(os, 1, foo, "THING", M_PI);
。 但是,我也有一些需要不以空格分隔的标记,如join(os, "KEY=", val);
。
我试着玩这些参数,想想也许我可以在参数列表中填充nullptr
并使用它来重载方法,跳过空间,但我不能为我的生活弄清楚如何做这种过载。
在标准合规性普遍存在之前,我所看到的关于可变参数模板的大多数问题都相当陈旧。 如果在其他地方已经回答了这个问题,请指出。 我正在使用GCC 7.3。
或者,告诉我,我过于复杂应该非常简单。 也许可变参数模板不是这个螺栓的正确锤子?
介绍一个帮手
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};
}
像它一样使用它
join(std::cout, "Hello", "World", pack("pi=", 3.14));
完整代码( 现场 )
#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));
}
请注意,通过聚合或继承将pack_t
基于std::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)...);
}
现在,您可以打包不同数量的参数( 实时 )。
我想你可以选择一种特殊的类型来跳过前面的空格; 例如,您可以定义no_space_tag
类或结构
struct no_space_tag {};
并添加一个join()
版本,该版本拦截第二个位置的no_space_tag
对象(第三个,计算os
)并且不在第一个(第二个)元素之后添加空格; 我的意思是
template <typename T, typename ... Args>
void join (std::ostream & os, T const & arg, no_space_tag const &,
Args ... args)
{
os << arg;
join(os, args...);
}
所以你可以用这种方式添加一个no_space_tag
对象
join(std::cout, "KEY=", no_space_tag{}, val);
或者也是这样
no_space_tag nst;
join(std::cout, "KEY=", nst, val, ',', "KEY=", nst, val2);
避免添加前面的空格。
以下是一个完整的工作示例
#include <iostream>
struct no_space_tag {};
template <typename T>
void join (std::ostream & os, T const & arg)
{ os << arg << '\000'; }
template <typename T, typename ... Args>
void join (std::ostream & os, T const & arg, Args ... args)
{
os << arg << " ";
join(os, args...);
}
template <typename T, typename ... Args>
void join (std::ostream & os, T const & arg, no_space_tag const &,
Args ... args)
{
os << arg;
join(os, args...);
}
int main()
{
char foo {'a'};
long val { 42L };
join(std::cout, 1, foo, "THING", "M_PI");
join(std::cout, "KEY=", no_space_tag{}, val);
}
你可以使用这样的东西。 First
参数要求您传递至少一个参数,但我认为这是合理的。
#include <iostream>
#include <array>
#include <ostream>
#include <cmath>
template<typename First, typename ... Rest>
void join(std::ostream& _s, bool _sep, First&& _f, Rest&& ... _rest)
{
_s << _f;
std::array<int, sizeof...(_rest)> status{(_s << (_sep ? " " : "") << std::forward<Rest>(_rest), 0) ...};
_s << '\n';
}
int main()
{
join(std::cout, true, "These", "are", "some", "args");
join(std::cout, true, "These", "are", 4, "args");
join(std::cout, true, "pi", "is", "a", "constant", "=", M_PI);
join(std::cout, false, "KEY=", "val");
return 0;
}
这打印
These are some args
These are 4 args
pi is a constant = 3.14159
KEY=val
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.