Ada*_*dam 112 c++ optimization performance stl c++-faq
std::swap()
在排序甚至分配期间被许多std容器(例如std::list
和std::vector
)使用.
但是std实现swap()
非常普遍,而且对于自定义类型来说效率很低.
因此,通过std::swap()
使用自定义类型特定实现进行重载可以获得效率.但是如何实现它以便std容器使用它?
Dav*_*ams 127
重载交换的正确方法是将其写入与交换相同的命名空间中,以便可以通过参数依赖查找(ADL)找到它.一件特别容易的事情是:
class X
{
// ...
friend void swap(X& a, X& b)
{
using std::swap; // bring in swap for built-in types
swap(a.base1, b.base1);
swap(a.base2, b.base2);
// ...
swap(a.member1, b.member1);
swap(a.member2, b.member2);
// ...
}
};
Run Code Online (Sandbox Code Playgroud)
How*_*ant 68
注意Mozza314
这是一个泛型std::algorithm
调用的效果的模拟std::swap
,并让用户在命名空间std中提供它们的交换.由于这是一个实验,这个模拟使用namespace exp
而不是namespace std
.
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
exp::swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
namespace exp
{
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
Run Code Online (Sandbox Code Playgroud)
对我来说这打印出来:
generic exp::swap
Run Code Online (Sandbox Code Playgroud)
如果您的编译器打印出不同的东西,那么它就不能正确地为模板实现"两阶段查找".
如果您的编译器符合(对C++ 98/03/11中的任何一个),那么它将给出我显示的相同输出.在这种情况下,确实会发生你所担心的事情.把你的swap
命名空间std
(exp
)放在一起并没有阻止它发生.
戴夫和我都是委员会成员,并且已经在这个标准领域工作了十年(而且并不总是彼此一致).但是这个问题已经解决了很长时间,我们都同意它是如何解决的.无视戴夫在这方面的专家意见/答案,这是你自己的危险.
在C++ 98发布之后,这个问题就暴露出来了.从2001年开始戴夫和我开始在这个领域工作.这是现代解决方案:
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
void swap(A&, A&)
{
printf("swap(A, A)\n");
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
Run Code Online (Sandbox Code Playgroud)
输出是:
swap(A, A)
Run Code Online (Sandbox Code Playgroud)
更新
已经观察到:
namespace exp
{
template <>
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
Run Code Online (Sandbox Code Playgroud)
作品!那么为什么不使用呢?
考虑一下你A
是一个类模板的情况:
// simulate user code which includes <algorithm>
template <class T>
struct A
{
};
namespace exp
{
template <class T>
void swap(A<T>&, A<T>&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A<int> a[2];
exp::algorithm(a, a+2);
}
Run Code Online (Sandbox Code Playgroud)
现在它再也不起作用了.:-(
所以你可以放入swap
命名空间std并让它工作.但是,你需要记住把swap
在A
的命名空间的情况下,当你有一个模板:A<T>
.而且因为如果你把这两种情况下将工作swap
中A
的命名空间,它只是更容易记住(并教其他人)只是做的一个方法.
Wil*_*lka 53
您不允许(通过C++标准)重载std :: swap,但是您特别允许将自己类型的模板特化添加到std命名空间.例如
namespace std
{
template<>
void swap(my_type& lhs, my_type& rhs)
{
// ... blah
}
}
Run Code Online (Sandbox Code Playgroud)
然后std容器(以及其他任何地方)的使用将选择你的专业而不是一般的.
另请注意,提供swap的基类实现对于派生类型来说还不够好.例如,如果你有
class Base
{
// ... stuff ...
}
class Derived : public Base
{
// ... stuff ...
}
namespace std
{
template<>
void swap(Base& lha, Base& rhs)
{
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
这将适用于Base类,但如果您尝试交换两个Derived对象,它将使用std中的泛型版本,因为模板化交换是完全匹配(并且它避免了仅交换派生对象的"base"部分的问题).
注意:我已经更新了这个以从我的上一个答案中删除错误的位.D'哦!(感谢puetzk和j_random_hacker指出来)
pue*_*tzk 29
虽然通常不应该向std :: namespace添加内容是正确的,但是特别允许为用户定义的类型添加模板特化.重载功能不是.这是一个微妙的区别:-)
17.4.3.1/1除非另有说明,否则C++程序未使用命名空间std向名称空间std或名称空间添加声明或定义.程序可以将任何标准库模板的模板特化添加到命名空间std.标准库的这种特化(完整或部分)会导致未定义的行为,除非声明取决于用户定义的外部链接名称,除非模板特化符合原始模板的标准库要求.
std :: swap的特殊化看起来像:
namespace std
{
template<>
void swap(myspace::mytype& a, myspace::mytype& b) { ... }
}
Run Code Online (Sandbox Code Playgroud)
如果没有模板<>位,它将是一个未定义的重载,而不是允许的特化.@Wilka建议更改默认命名空间的方法可能与用户代码一起使用(由于Koenig查找更喜欢无命名空间版本)但是它不能保证,实际上并不是真的应该(STL实现应该完全使用-qualified std :: swap).
有一个.在comp.lang.c线++主持一个长期的话题标准探讨.其中大部分是关于部分专业化的(目前还没有好办法).
归档时间: |
|
查看次数: |
29002 次 |
最近记录: |