15

C++ 標準では、名前空間で型を宣言したり何かを定義したりすることは禁止されstdていますが、ユーザー定義型の標準 STL テンプレートを特殊化することは許可されています。

std::swap通常、独自のカスタム テンプレート型に特化したい場合は、次のようにします。

namespace std
{
  template <class T>
  void swap(MyType<T>& t1, MyType<T>& t2)
  {
     t1.swap(t2);
  }
}

...そして、それはうまくいきます。しかし、私の通常の実践が標準に準拠しているかどうかは完全にはわかりません。私はこれを正しくやっていますか?

4

7 に答える 7

16

あなたが持っているのは専門化ではなく、過負荷であり、まさに標準が禁止しているものです。(ただし、現在のところほとんどの場合、実際に機能し、受け入れられる場合があります。)

クラス テンプレートに独自のスワップを提供する方法は次のとおりです。

template<class T>
struct Ex {
  friend void swap(Ex& a, Ex& b) {
    using std::swap;
    swap(a.n, b.n);
  }
  T n;
}

そして、ここで swap を呼び出す方法を示します。Ex の swap でも使用されていることがわかります。

void f() {
  using std::swap; // std::swap is the default or fallback
  Ex<int> a, b;
  swap(a, b); // invokes ADL
}

関連:関数テンプレートの特殊化の重要性と必要性

于 2010-02-08T16:56:07.920 に答える
4

MyType の名前空間で swap を定義して、引数に依存するルックアップ機能を利用しないのはなぜですか?

于 2010-02-08T16:55:02.303 に答える
3

引数依存 (別名 Koenig) ルックアップのため、必要な型の名前空間で独自のスワップを指定できると思います::std::swap::std::swapまた、独自の swap メンバー関数を持つクラスではテンプレートの展開が異なるため、そのメンバー関数をクラスに追加して、それを型に使用できると思います。

于 2010-02-08T16:56:09.697 に答える
2

編集

Scott Meyer の記事を参照してください。私の回答の確認については、Effective C++ 3rd Editionの項目 25: 非スロー スワップのサポートを検討してください (p106-p112)を参照してください。

元の答え

Scott Meyers がこれについて書いているので、私の答えは記憶から来ています。

まず、クラスの名前空間で swap 関数を定義します。例えば ​​:

namespace MyNamespace
{
   class MyClass { /* etc. */ } ;

   template<typename T>
   class MyTemplate { /* etc. */ } ;

   void swap(MyClass & lhs, MyClass & rhs)
   {
      // the swapping code (**)
   }

   template<typename T>
   void swap(MyTemplate<T> & lhs, MyTemplate<T> & rhs)
   {
      // the swapping code (**)
   }
}

次に、可能であれば(テンプレート化されたクラス (*) では常に可能であるとは限りません)、名前空間 std で swap 関数を特殊化します。例えば ​​:

namespace std
{
   template<>
   void swap<MyNamespace::MyClass>(MyNamespace::MyClass & lhs, MyNamespace::MyClass & rhs)
   {
      // the swapping code (**)
   }

   // The similar code for MyTemplate is forbidden, so don't try
   // to uncomment it
   //
   // template<typename T>
   // void swap<MyNamespace::MyTemplate<T> >(MyNamespace::MyTemplate<T> & lhs, MyNamespace::MyTemplate<T> & rhs)
   // {
   //   // the swapping code (**)
   // }
}

swap 関数を使用する場合は、std swap 関数をスコープにインポートして間接的に行います。例えば ​​:

void doSomething(MyClass & lhs, MyClass & rhs)
{
   // etc.

   // I swap the two objects below:
   {
      using std::swap ;
      swap(lhs, rhs) ;
   }

   // etc.
}

void doSomethingElse(MyTemplate<int> & lhs, MyTemplate<int> & rhs)
{
   // etc.

   // I swap the two objects below:
   {
      using std::swap ;
      swap(lhs, rhs) ;
   }

   // etc.
}

本にアクセスできるようになり次第、正確な参照をここに投稿します。

  • (*) テンプレートの関数の部分特化は禁止
  • (**) もちろん、クラスで「swap」メソッドを宣言し、swap 関数で swap メソッドを呼び出し、ユーザーが swap 関数を呼び出すのが良いパターンです。
于 2010-02-08T17:54:42.193 に答える
2

あなたがしていることはオーバーロードであり、テンプレートの特殊化ではありません。標準では、内部でオーバーロードすることは許可されていませんnamespace std(17.6.4.2.1 §1)

C++ プログラムの動作は、特に指定されていない限りnamespace std、名前空間に宣言または定義を追加した場合、未定義です。プログラムは、宣言がユーザー定義型に依存し、特殊化が元のテンプレートの標準ライブラリ要件を満たし、明示的に禁止されていない場合namespace stdにのみ、標準ライブラリ テンプレートのテンプレート特殊化を追加できます。namespace std

したがって、テンプレートの型を独自の名前空間に配置し、その名前空間内で非メンバーを定義することをお勧めswap()します (これは厳密には必要ではありませんが、良い方法です)。この方法は、または名前空間にあるswap(x,y)場合、引数依存ルックアップ (ADL、別名 Koenig ルックアップ) を介してどこからでも機能します。xy

namespace my_ns {

template <typename T> class MyType
{
public:
    void swap( MyType & other ) noexcept;
};

template <typename T>
void swap( MyType<T> & lhs, MyType<T> & rhs ) noexcept
{
    lhs.swap(rhs);
}

} // namespace my_ns

コードを使用swap()する場合は、通常、このusing namespace std手法を使用する必要があります。このようにして、スワップのバージョンが ADL によって検出され、std::swap()より特殊化されているため、関数よりも優先されます。

// client code
MyType<Bla> x, y;
/* ... some code ... */
using namespace std;
swap( x, y ); // will call your swap version
于 2013-02-08T09:47:55.223 に答える
0

同じ名前空間でタイプとスワップ関数を定義します。

namespace foo
{
   struct Bar
   {
   };

   void swap(Bar & t1, Bar& t2)
   {
     // whatever
   }
}

int main()
{
    using std::swap;
    foo::Bar a, b;
    swap(a, b); // Argument-dependent lookup chooses foo::swap
                // if it exists, or else reverts to std::swap
}
于 2010-02-08T16:56:16.583 に答える
-1

独自の を定義しswapます。この関数は、自分の型以外の任意の型 T に対して std::swap を呼び出す必要があります。

namespace help // my namespace
{ 

  template <class T> 
  void swap(T& t1, T& t2) 
  { 
     ::std::swap(t1, t2);  // Redirect to std for almost all cases
  } 

  // My special case: overloading
  template <class T> 
  void swap(MyType<T>& t1, MyType<T>& t2) 
  { 
     t1.swap(t2); 
  } 

}  //  namespace help 

// Sample
int main() 
{

   MyType<int> t1, t2; // may be add initialization
   int i1=5, i2=7;

   help::swap(t1, t2); //  Your swap
   help::swap(i1, i2); //  Redirect to std::swap
}
于 2010-02-08T17:41:17.237 に答える