8

私はこのプログラムを持っています

#include <iostream>
#include <sstream>
#include <iterator>
#include <vector>
#include <algorithm>
using namespace std ;

#if 0
namespace skg 
{
 template <class T>
  struct Triplet ;
}

template <class T>
ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t) ;
#endif

namespace skg
{
 template <class T>
  struct Triplet
  {
 //  friend ostream& ::operator<< <> (ostream& os, const Triplet<T>& p_t) ;

   private:
   T x, y, z ;

   public:
   Triplet (const T& p_x, const T& p_y, const T& p_z)
    : x(p_x), y(p_y), z(p_z) { }
  } ;
}

template <class T>
ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t)
{
 os << '(' << p_t.x << ',' << p_t.y << ',' << p_t.z << ')' ;
 return os ;
}

namespace {
 void printVector()
 {
  typedef skg::Triplet<int> IntTriplet ;

  vector< IntTriplet > vti ;
  vti.push_back (IntTriplet (1, 2, 3)) ;
  vti.push_back (IntTriplet (5, 5, 66)) ;

  copy (vti.begin(), vti.end(), ostream_iterator<IntTriplet> (cout, "\n")) ;
 }
}
int main (void)
{
 printVector() ;
}

コンパイラが skg::Triplet の出力演算子を見つけられなかったため、コンパイルが失敗しました。ただし、出力演算子は存在します。

トリプレットを skg 名前空間からグローバル名前空間に移動すると、すべて正常に動作します。ここで何が悪いのですか?

4

1 に答える 1

13

operator<<の実装をクラスと同じ名前空間に移動する必要があります。それは探しています:

ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t)

しかし、引数依存ルックアップ (ADL) の欠点のため、それを見つけることができません。ADL は、無料の関数を呼び出すと、引数の名前空間でその関数を探すことを意味します。これは、私たちができるのと同じ理由です:

std::cout << "Hello" << std::endl;

名前空間operator<<(std::ostream&, const char*)にありますが。stdあなたの呼び出しでは、これらの名前空間はstdskgです。

skg(あなたのものはグローバルスコープにあるため)で1つを見つけるのではなく、両方を調べてから、 を調べますstd。可能性 (すべての法線operator<<) が表示されますが、一致するものはありません。実行中のコード ( のコードostream_iterator) は名前空間stdにあるため、グローバル名前空間へのアクセスは完全になくなります。

オペレーターを同じ名前空間に配置することで、ADL が機能します。これについては、Herb Sutter の記事「A Modest Proposal: Fixing ADL」で説明されています。. (PDF). 実際、記事の抜粋を次に示します (欠点を示しています)。

// Example 2.4
//
// In some library header:
//
namespace N { class C {}; }
int operator+( int i, N::C ) { return i+1; }

// A mainline to exercise it:
//
#include <numeric>
int main() {
    N::C a[10];
    std::accumulate( a, a+10, 0 ); // legal? not specified by the standard
}

あなたと同じ状況です。

Sutter と & Alexandrescu による本「C++ Coding Standards」には、次のような有用なガイドラインがあります。

  1. 型とその非メンバー関数インターフェイスを同じ名前空間に保持します。

それに従ってください、あなたと ADL は幸せになります。この本をお勧めします。入手できない場合でも、少なくとも上でリンクした PDF を読んでください。必要な関連情報が含まれています。


オペレーターを移動した後、friend ディレクティブが必要になることに注意してください (プライベート変数にアクセスできるようにするため)。

template <typename U>
friend ostream& operator<< (ostream& os, const Triplet<U>& p_t);

そしてタダ!修理済み。

于 2010-01-29T03:35:38.650 に答える