Подтвердить что ты не робот

Перегрузка глобальной свопинга для пользовательского типа

Стандарт С++ запрещает объявление типов или определение чего-либо в пространстве имен std, но это позволяет вам специализировать стандартные шаблоны STL для пользовательских типов.

Обычно, когда я хочу специализировать std::swap для моего собственного шаблонного шаблона, я просто делаю:

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

... и это отлично работает. Но я не совсем уверен, стандартная ли моя обычная практика. Правильно ли я делаю это?

4b9b3361

Ответ 1

То, что у вас есть, не специализация, это перегрузка и именно то, что запрещает стандарт. (Тем не менее, он почти всегда в настоящее время работает на практике и может быть приемлемым для вас.)

Вот как вы предоставляете свой собственный своп для вашего шаблона класса:

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
}

Связанный: Значение и необходимость специализации шаблона функций

Ответ 2

Почему вы просто не определяете swap в пространстве имен MyType и не используете зависящую от аргументов мощность поиска?

Ответ 3

Из-за зависимого от аргумента (например, Koenig) поиска, я считаю, что вы можете указать свой собственный своп в пространстве имен типа, в котором вы хотите, и он будет найден в предпочтении ::std::swap. Кроме того, я считаю, что шаблон для ::std::swap будет расширяться по-разному для классов, у которых есть своя функция swap-члена, и поэтому вы можете добавить эту функцию-член в класс и которая будет использоваться для вашего типа.

Ответ 4

Изменить

См. статью Скотта Мейера: см. Эффективное С++ 3rd Edition, пункт 25: рассмотрите поддержку не-метательного свопа (p106-p112) для подтверждения моего ответа.

Оригинальный ответ

Скотт Майерс написал об этом, поэтому мой ответ исходит из памяти.

Сначала определите функцию подкачки в пространстве имен вашего класса. Например:

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 (**)
   }
}

Затем , если возможно (это не всегда возможно для шаблонных классов (*)), специализируйте функцию swap в пространстве имен std. Например:

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 (**)
   // }
}

При использовании функции подкачки косвенно коснитесь, импортируя функцию 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", объявленный в классе, иметь функции свопинга, вызывать метод подкачки, и пользователь вызывает функцию свопинга.

Ответ 5

То, что вы делаете, - это перегрузка, а не специализация шаблона. Стандарт не позволяет перегружать внутри namespace std (17.6.4.2.1 §1)

Поведение программы на С++ undefined, если оно добавляет объявления или определения к namespace std или к пространству имен внутри namespace std, если не указано иное. Программа может добавить специализацию шаблона для любого стандартного шаблона библиотеки к namespace std только в том случае, если объявление зависит от пользовательского типа, а специализация соответствует стандартным требованиям библиотеки для исходного шаблона и явно не запрещена.

Поэтому, предпочитайте поместить свой тип шаблона в собственное пространство имен и определить не-член swap() внутри этого пространства имен (это не является строго необходимым, но хорошей практикой). Таким образом, swap(x,y) будет работать из любого места через зависимый от аргумента поиск (ADL, aka Koenig lookup), если x или y находятся в вашем пространстве имен.

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. Таким образом, ваша версия swap будет найдена ADL, и она будет предпочтительнее для функции std::swap(), поскольку она более специализирована.

// client code
MyType<Bla> x, y;
/* ... some code ... */
using namespace std;
swap( x, y ); // will call your swap version

Ответ 6

Определите свой тип и свою функцию свопинга в том же пространстве имен:

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
}

Ответ 7

Определите собственный swap. Эта функция должна вызывать std:: swap для любого типа T, кроме ваших типов.

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
}