为用户定义的类型重载全局交换

时间:2010-02-08 16:51:12

标签: c++ stl swap standards

C ++标准禁止在命名空间std中声明类型或定义任何内容,但它允许您为用户定义的类型专门化标准STL模板。

通常,当我想将std::swap专门用于我自己的自定义模板类型时,我只是这样做:

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

......这很好。但我不完全确定我的惯常做法是否符合标准。我这样做了吗?

7 个答案:

答案 0 :(得分:15)

你拥有的不是专业化,它是超载,而且正是标准禁止的。 (但是,它几乎总是在实践中起作用,并且可能为您所接受。)

以下是为类模板提供自己的交换方式:

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

以下是你如何调用swap,你会注意到它也用于Ex的交换:

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

相关:Function template specialization importance and necessity

答案 1 :(得分:3)

为什么不在MyType的命名空间中定义swap并利用依赖于参数的查找功能?

答案 2 :(得分:3)

由于依赖于参数(也就是Koenig)查找,我相信你可以在你想要它的类型的命名空间中指定你自己的交换,它将优先于::std::swap。此外,我相信::std::swap的模板将针对具有自己的交换成员函数的类进行不同的扩展,因此您可以将该成员函数添加到类中,并将用于您的类型。

答案 3 :(得分:1)

修改

请参阅Scott Meyer的文章:参见 Effective C ++ 3rd Edition ,第25项:考虑支持非投掷交换(p106-p112)以确认我的答案

原始答案

Scott Meyers写了这篇文章,所以我的答案来自记忆。

首先,在类的命名空间中定义交换函数。例如:

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中专门化交换函数。例如:

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交换功能导入范围。例如:

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函数。

答案 4 :(得分:0)

您正在做的是重载而不是模板专业化。该标准不允许您在namespace std(17.6.4.2.1§1)

内重载
  

如果C ++程序将声明或定义添加到namespace stdnamespace std中的命名空间,除非另有说明,否则未定义C ++程序的行为。只有当声明取决于用户定义的类型并且专业化符合原始模板的标准库要求且未明确禁止时,程序才可以将任何标准库模板的模板专门化添加到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

答案 5 :(得分:-1)

在同一命名空间中定义您的类型和交换函数:

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
}

答案 6 :(得分:-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
}