C ++ 03では、さまざまな可能性があります。
- 0-N引数のオーバーロードを生成します(たとえば、Boost.Preprocessorを使用)
- 短所リストを使用する(
cons(1)("some string")(foo)
)
- オブジェクトを使用し、いくつかの演算子をオーバーロードします(
operator()
たとえば、operator%
Boost.Formatなど)
最初のオプションは少し注意が必要です。誰もがマクロを簡単に理解できるわけではないので、すぐにC ++ 0xに移行する予定がある場合は、短期的なソリューションのためだけに予約します。
3番目のオプションは素晴らしいカスタムタッチを提供するかもしれません(フォーマットは%
多くの言語のサインで行われます)が、それはまた、この特定の「可変個引数」関数が毎回どのように機能するかを覚えておく必要があることを意味します。
私の個人的な好みは、cons
両方の問題を解決するアプローチです。
- 定義にはテンプレートのみが含まれるため、1よりも読みやすく保守しやすくなります。
- cons-machineryを一度定義すると、それを任意の「可変個引数」関数に再利用できるため(関数のまま)、一貫性が高まり、作業を節約できます。
たとえば、次のように機能します。
この例で使用するインクルード:
#include <cassert>
#include <iostream>
#include <string>
値を追加する結果タイプのヘルパー(追加するとより効率的になる可能性がありますが、それは直感に反する逆の順序で引数を渡すことを意味します):
template <typename T, typename Next> struct Cons;
struct ConsEmpty;
template <typename Cons, typename U>
struct cons_result;
template <typename U>
struct cons_result<ConsEmpty, U> {
typedef Cons<U, ConsEmpty> type;
};
template <typename T, typename U>
struct cons_result<Cons<T, ConsEmpty>, U> {
typedef Cons<T, Cons<U, ConsEmpty> > type;
};
template <typename T, typename Next, typename U>
struct cons_result<Cons<T, Next>, U> {
typedef Cons<T, typename cons_result<Next, U>::type> type;
};
テンプレート自体、値を追加Cons
する魔法があります。operator()
別のタイプの新しいアイテムが作成されることに注意してください。
template <typename T, typename Next>
struct Cons {
Cons(T t, Next n): value(t), next(n) {}
T value;
Next next;
template <typename U>
typename cons_result<Cons, U>::type operator()(U u) {
typedef typename cons_result<Cons, U>::type Result;
return Result(value, next(u));
}
};
struct ConsEmpty {
template <typename U>
Cons<U, ConsEmpty> operator()(U u) {
return Cons<U, ConsEmpty>(u, ConsEmpty());
}
};
template <typename T>
Cons<T, ConsEmpty> cons(T t) {
return Cons<T, ConsEmpty>(t, ConsEmpty());
}
それで再考VarPrint
:
bool VarPrint(std::ostream& out, const std::string& s, ConsEmpty) {
std::string::size_type offset = 0;
if((offset = s.find("%")) != std::string::npos) {
if(offset == s.size() - 1 || s[offset + 1] != '%') {
assert(0 && "Missing Arguments!");
return false;
}
}
out << s;
return true;
}
template<typename T, typename Next>
bool VarPrint(std::ostream& out,
std::string const& s,
Cons<T, Next> const& cons)
{
std::string::size_type prev_offset = 0, curr_offset = 0;
while((curr_offset = s.find("%", prev_offset)) != std::string::npos) {
out << s.substr(prev_offset, curr_offset);
if(curr_offset == s.size() - 1 || s[curr_offset + 1] != '%') {
out << cons.value;
if(curr_offset + 2 < s.length())
return VarPrint(out, s.substr(curr_offset + 2), cons.next);
return true;
}
prev_offset = curr_offset + 2;
if(prev_offset >= s.length())
break;
}
assert(0 && "Extra Argument Provided!");
return false;
}
そしてデモ:
int main() {
VarPrint(std::cout, "integer %i\n", cons(1));
VarPrint(std::cout, "mix of %i and %s\n", cons(2)("foo"));
}
出力:
integer 1
mix of 2 and foo