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

发布于 2024-08-20 06:58:25 字数 349 浏览 5 评论 0原文

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

通常,当我想为我自己的自定义模板类型专门化 std::swap 时,我只需这样做:

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

...并且效果很好。但我并不完全确定我通常的做法是否符合标准。我这样做正确吗?

The C++ standard prohibits declaring types or defining anything in namespace std, but it does allow you to specialize standard STL templates for user-defined types.

Usually, when I want to specialize std::swap for my own custom templated type, I just do:

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

...and that works out fine. But I'm not entirely sure if my usual practice is standard compliant. Am I doing this correctly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

无可置疑 2024-08-27 06:58:25

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

以下是您如何为类模板提供自己的交换:

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

这是您如何调用交换,您会注意到它在 Ex 中使用也交换:

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

相关:函数模板专业化的重要性和必要性

What you have is not a specialization, it is overloading and exactly what the standard prohibits. (However, it will almost always currently work in practice, and may be acceptable to you.)

Here is how you provide your own swap for your class template:

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

And here is how you call swap, which you'll notice is used in Ex's swap too:

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

Related: Function template specialization importance and necessity

少女的英雄梦 2024-08-27 06:58:25

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

Why won't you just define swap in MyType's namespace and exploit argument-dependent lookup power?

九公里浅绿 2024-08-27 06:58:25

由于依赖于参数(又名 Koenig)查找,我相信您可以在所需类型的命名空间中指定您自己的交换,并且它将优先于 ::std::swap 找到。另外,我相信 ::std::swap 的模板对于具有自己的交换成员函数的类会有不同的扩展,因此您可以将该成员函数添加到类中,并将其用于您的类型。

Because of argument dependent (aka Koenig) lookup, I believe you can specify your own swap in the namespace of the type you want it for and it will be found in preference to ::std::swap. Also, I believe the template for ::std::swap will expand differently for classes that have their own swap member function and so you can add that member function to the class and that will be used for your type.

美胚控场 2024-08-27 06:58:25

编辑

请参阅 Scott Meyer 的文章:请参阅Effective C++ 第三版,第 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 (**)
   }
}

然后,如果可能(模板类 (*) 并不总是可能),在命名空间 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.
}

一旦我可以访问我的书,我就会在此处发布确切的参考文献。

  • (*) 禁止函数的模板部分特化
  • (**) 当然,一个好的模式是在类中声明一个“交换”方法,让交换函数调用交换方法,并让用户调用交换方法功能。

Edit

See Scott Meyer's article: See Effective C++ 3rd Edition, item 25: Consider support for a non-throwing swap (p106-p112) for a confirmation of my answer.

Original answer

Scott Meyers wrote about this, so my answer comes from memory.

First, define a swap function in the namespace of your class. For example :

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

Then, if possible (it is not always possible for templated classes (*) ), specialize the swap function in the namespace std. For example :

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

The, when using the swap function, do it indirectly, importing the std swap function into your scope. For example :

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.
}

As soon as I have access to my books, I'll post here the exact reference.

  • (*) template partial specialization of a function is forbidden
  • (**) of course, a good pattern is to have a "swap" method declared in the class, have the swap functions call the swap method, and have the user call the swap function.
待"谢繁草 2024-08-27 06:58:25

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

如果 C++ 程序向 namespace stdnamespace std 内的命名空间添加声明或定义,则其行为是未定义的,除非另有说明。仅当声明依赖于用户定义的类型并且专门化满足原始模板的标准库要求并且未明确禁止时,程序才可以将任何标准库模板的模板专门化添加到命名空间 std .

因此,最好将模板类型放入您自己的命名空间中,并在该命名空间内定义一个非成员 swap() (这不是绝对必要的,但很好的做法)。这样,如果 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

What you're doing is an overload and not a template specialization. The standard does not allow you to overload inside namespace std (17.6.4.2.1 §1)

The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

Therefore, prefer to put your template type into your own namespace and define a non-member swap() within that namespace (this is not strictly necessary, but good practice). This way swap(x,y) will work from anywhere via argument dependent lookup (ADL, aka Koenig lookup), if x or y are in your namespace.

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

Code using swap() should normally use the using namespace std technique. This way your version of swap will be found by ADL and it will be prefered to the std::swap() function, since it is more specialized.

// client code
MyType<Bla> x, y;
/* ... some code ... */
using namespace std;
swap( x, y ); // will call your swap version
贱贱哒 2024-08-27 06:58:25

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

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
}

Define your type and your swap function in the same namespace:

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
}
醉南桥 2024-08-27 06:58:25

定义自己的交换。此函数必须为除您的类型之外的任何类型 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
}

Define own swap. This function must call std::swap for any type T except your types.

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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文