2011-01-27 8 views
3

私の好みに合わせてSTLコンテナを文字列に変換するAsString()関数を記述しようとしています。ここで私は、これまでに作ってみたコードです:あなたが見ることができるようにC++でSTLコンテナを印刷するための再帰的なAsString()

template<class T> 
inline string AsString(const T& v); 

template<class First, class Second> 
inline string AsString(const pair<First, Second>& p); 

template<class Iter> 
inline string PrintSequence(const char* delimiters, Iter begin, Iter end) { 
    string result; 
    result += delimiters[0]; 
    int size = 0; 
    for (size = 0; begin != end; ++size, ++begin) { 
    if (size > 0) { 
     result += ", "; 
    } 
    result += AsString(*begin); 
    } 
    result += delimiters[1]; 
    result += StringPrintf("<%d>", size); 
    return result; 
} 

#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \ 
template<class T1, class T2> \ 
inline string AsString(const Sequence<T1, T2>& seq) { \ 
    return PrintSequence("[]", seq.begin(), seq.end()); \ 
} 

OUTPUT_TWO_ARG_CONTAINER(vector) 
OUTPUT_TWO_ARG_CONTAINER(deque) 
OUTPUT_TWO_ARG_CONTAINER(list) 

template<class First, class Second> 
inline string AsString(const pair<First, Second>& p) { 
    return "(" + AsString(p.first) + ", " + AsString(p.second) + ")"; 
} 

template<class T> 
inline string AsString(const T& v) { 
    ostringstream s; 
    s << v; 
    return s.str(); 
} 

は、基本的な考え方は、AsString()が再帰的にSTLコンテナの上に自分自身を呼び出し、それは私がドンいつもoperator<<()(理由に底打ちということですoperator<<()を上書きしたくないのは、まさにそうする他のライブラリに干渉したくないからです。

は今、AsString()コンパイルおよび浅い容器の上ではなく、ネストされたもので動作します:

vector<int> v; 
v.push_back(1); 
v.push_back(2); 
AsString(v) == "[1, 2]<2>"; // true 

vector<vector<int> > m; 
m.push_back(v); 
m.push_back(v); 
AsString(m) == "[[1, 2]<2>, [1, 2]<2>]<2>"; // Compilation Error!!! 

コンパイラは、何らかの理由で、 `M」の要素を印刷しようとしたときにもかかわらず、operator<<()を使用したいです私はベクトルのためのテンプレートの特殊化を提供しているという事実です。

どうすればAsString()を動作させることができますか?

UPDATE:OK、定義の順序は問題ありません(少なくともこのコンパイラでは - gcc 4.4.3)。マクロ定義を最初に置くと、コンパイラはそれらを正しくピックアップし、ベクトルのベクトルを表示します。不可解。

+0

マイナー発言:後でなくコンテナの前にサイズを入れておくと、再解析が簡単になります。そうでなければきちんとしたコード(テンプレート関数の 'inline'を削除するだけです)は不要です。 –

答えて

1

には、のAsStringが割り当てられています。それが起こると、後のオーバーロードはT const &バージョンよりも優先されません。

代わりに、さまざまなstdlibコンテナのoverload op<< in a special namespace。あなたが他のコードには影響を与えないように、名前空間は重要ですが、明示的AsStringでそれを使用します。

namespace make_sure_to_put_these_overloads_in_a_namespace { 

// Your PrintSequence adapted to a stream instead of a string: 
template<class Iter> 
void PrintSequence(std::ostream &s, const char* delim, 
        Iter begin, Iter end) 
{ 
    s << delim[0]; 
    int size = 0; 
    if (begin != end) { 
    s << *begin; 
    ++size; 
    while (++begin != end) { 
     s << ", " << *begin; 
     ++size; 
    } 
    } 
    s << delim[1] << '<' << size << '>'; 
} 

#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \ 
template<class T1, class T2> \ 
std::ostream& operator<<(std::ostream &s, Sequence<T1, T2> const &seq) { \ 
    PrintSequence(s, "[]", seq.begin(), seq.end()); \ 
    return s; \ 
} 

OUTPUT_TWO_ARG_CONTAINER(std::vector) 
OUTPUT_TWO_ARG_CONTAINER(std::deque) 
OUTPUT_TWO_ARG_CONTAINER(std::list) 
// other types 
#undef OUTPUT_TWO_ARG_CONTAINER 

template<class First, class Second> 
std::ostream& operator<<(std::ostream &s, std::pair<First, Second> const &p) { \ 
    s << "(" << p.first << ", " << p.second << ")"; 
    return s; 
} 

} 

template<class T> 
std::string AsString(T const &v) { 
    using namespace make_sure_to_put_these_overloads_in_a_namespace; 
    std::ostringstream ss; 
    ss << v; 
    return ss.str(); 
} 
+0

私はその答えを理解しているか分からない。あなたは、私の頭を回転させるので、オペレータの過負荷になると、名前空間解決ビジネスに入ることは望ましくありません。新しい名前空間を導入せずにこの問題を解決することは可能でしょうか?また、 'operator <<()'をオーバーロードしなくても可能ですか? –

+0

@LajosNagy:これが最も簡単な解決策です。あなたがこの答えについて理解していないことについて質問がある場合、私はそれらに対処することができます。 –

+0

OK、わかりません。あなたは私が_specialize_しなかったのはどういう意味ですか、むしろ私はAsString()を_overloaded_しました。 'AsString()'は(例のような)浅いコンテナでは動作しますが、ネストされたコンテナでは動作しないのはなぜですか?コンパイラは、浅いベクトルの 'AsString()'を選択しますが、入れ子のベクトルの要素は選択しません。 –

3

テンプレートの世界は素晴らしいです...そして不注意に本当のトラップ...

特殊化は、既存のテンプレート関数を受け取り、すべての引数を指定しています。

オーバーロードは、別の引数のセットに対して別の関数(テンプレートであるかどうかにかかわらず)と同じ名前を再利用しています。

template <typename T> 
void foo(T const& t); 

template <> 
void foo<int>(int i); // this is a "complete" specialization 

template <typename T, typename U> 
void foo<std::pair<T,U>>(std::pair<T,U> const& pair); 
    // this is a "partial" specialization 
    // and by the way... it does NOT COMPILE 

template <typename T, typename U> 
void foo(std::pair<T,U> const& pair); // this is an overload 

注構文差は、過負荷識別子(ここでfoo)後には<xxxx>がありません。

C++では、関数を部分的に特殊化することはできません。それは議論にいくらかの一般性を残すことです。あなたのいずれかを行うことができ、過負荷または完全に特化:GotW #49: Template Specialization and Overloading

この時点で必須読書したがって、選択が間にある:

template <typename T> 
std::string AsString(const T& v); // (1) 

template <typename T, typename Allocator> 
std::string AsString(std::vector<T, Allocator> const& v); // (2) 

そして、本当の問題は:*beginの種類は何ですか?

  • Iterが論理的にstd::vector< std::vector<int> >::iteratorです:

    まあ、mはconst修飾ではありません。

  • *beginのタイプように、2つのオーバーロードを有すると考えられるこのようstd::vector<int>&

ある:

  • (1):T = std::vector<int>、CONST-REFに
  • の変換が必要(2): T = int, U = std::allocator<int>、const-refへの変換が必要です

2番目は、私が理解する限り、実際のタイプに近いです。 VC++ 2010でテストしたところ、実際に選択されました。

ベクトルオーバーロードのconst修飾されていないバージョンを宣言して、コンパイラが魅力的かどうかを確認できますか? (私はちなみに、その名前を知りたい))。

+0

これは正しく聞こえますが、OPが使用しているコンパイラのADLのバグです。私は当然、理由としてコンパイラのバグを示唆することを躊躇していましたが、私は2010年前のことを知っていて、他のコンパイラはこの領域のテンプレートでバグを持っています。 –

+2

@Fred:はい、ADLテンプレートに加えて、オーバーロードの解像度は通常脆いです。これは、これをカバーするルールのセットを私に驚かせるものではありません。また、値の参照とバリデーションテンプレート(argh)で悪化しています。私は、どの機能が選択されるかを予測することは、もはや自分自身を信用していません。/ –

+0

gcc-4.4.3-glibc-2.11.1です。ところで、宣言命令はそれで何かをしなければならないと思いますか? –

関連する問題