簡體   English   中英

可變遞歸模板

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM