C++11 是否会更改显式调用 std::swap 的行为以确保找到位于 ADL 的交换区(如 boost::swap)?
背景
考虑以下代码:
#include <utility>
namespace ns
{
struct foo
{
foo() : i(0) {}
int i;
private:
foo(const foo&); // not defined,
foo& operator=(const foo&); // non-copyable
};
void swap(foo& lhs, foo& rhs)
{
std::swap(lhs.i, rhs.i);
}
}
template <typename T>
void do_swap(T& lhs, T& rhs); // implementation to be determined
int main()
{
ns::foo a, b;
do_swap(a, b);
}
在 C++03 中,do_swap
的实现将被视为“损坏”:
template <typename T>
void do_swap(T& lhs, T& rhs)
{
std::swap(lhs, rhs);
}
通过显式指定 std::
,它会禁止 ns ::swap
通过依赖于参数的查找来找到。 (然后它无法编译,因为 std::swap
尝试复制 foo
,这是不允许的。)相反,我们这样做:
template <typename T>
void do_swap(T& lhs, T& rhs)
{
using std::swap; // allow std::swap as a backup if ADL fails to find a swap
swap(lhs, rhs); // unqualified call to swap, allow ADL to operate
}
现在 ns:: swap
被发现,并且 std::swap
由于不太专业,没有被使用。它比较丑陋,但它有效,并且事后看来是可以理解的。 boost::swap
很好地为我们解决了这个问题(并提供了数组重载):
#include <boost/swap.hpp>
template <typename T>
void do_swap(T& lhs, T& rhs)
{
boost::swap(lhs, rhs); // internally does what do_swap did above
}
问题
std::swap
是否具有 boost::swap< 的行为/code> 在 C++11 中?如果没有,为什么?
对我来说,显然应该这样做。任何因更改而损坏的代码一开始都可能非常脆弱(算法和容器,如 std::sort 和 std::vector 未指定;允许实现调用 ADL 交换或不确定),因此更改会变得更好。此外,std::swap 现在是为数组定义的,因此进行更改当然不是不可能的。
然而,虽然 §17.6.3.2 规定标准库中对 swap
的所有调用都必须在没有 std::
限定的情况下完成(解决上述算法和容器的问题) ,它无法触及 std::swap 本身。它甚至提供了交换值的示例,其中包括 using std::swap;
。同样,§20.2.2(其中指定了 std::swap
)没有提及 ADL。
最后,GCC 在其 std::swap 实现中没有启用 ADL(MSVC 也没有,但这并没有说明太多)。所以我肯定是错误的,std::swap
采取了boost::swap
的行为,但我不明白为什么没有进行更改。 :( 而且我并不孤单!
Background
Consider the following code:
#include <utility>
namespace ns
{
struct foo
{
foo() : i(0) {}
int i;
private:
foo(const foo&); // not defined,
foo& operator=(const foo&); // non-copyable
};
void swap(foo& lhs, foo& rhs)
{
std::swap(lhs.i, rhs.i);
}
}
template <typename T>
void do_swap(T& lhs, T& rhs); // implementation to be determined
int main()
{
ns::foo a, b;
do_swap(a, b);
}
In C++03, this implementation of do_swap
would be considered "broken":
template <typename T>
void do_swap(T& lhs, T& rhs)
{
std::swap(lhs, rhs);
}
By explicitly specifying std::
, it prohibits ns::swap
from being found via argument-dependent lookup. (It then fails to compile because std::swap
tries to copy a foo
, which is not allowed.) Instead, we do this:
template <typename T>
void do_swap(T& lhs, T& rhs)
{
using std::swap; // allow std::swap as a backup if ADL fails to find a swap
swap(lhs, rhs); // unqualified call to swap, allow ADL to operate
}
Now ns::swap
is found and std::swap
, being less specialized, is not used. It's uglier, but it works and is understandable in hind-sight. boost::swap
wraps this up nicely for us (and provides array overloads):
#include <boost/swap.hpp>
template <typename T>
void do_swap(T& lhs, T& rhs)
{
boost::swap(lhs, rhs); // internally does what do_swap did above
}
Question
Does std::swap
take on the behavior of boost::swap
in C++11? If not, why?
To me it seems obvious that it ought to. Any code broken by the change was probably quite flimsy in the first place (algorithms and containers, like std::sort
and std::vector
, were underspecified; implementations were allowed to call ADL swap's or not indeterminately), so the change would be for the better. Additionally, std::swap
is now defined for arrays, so change at all certainly isn't out of the question.
However, while §17.6.3.2 specifies that all calls to swap
within the standard library must be done without std::
qualification (fixing the problem with algorithms and containers noted above), it fails to touch on std::swap
itself. It even gives examples of swapping values that include using std::swap;
. Likewise §20.2.2 (where std::swap
is specified) doesn't say a word on ADL.
Lastly, GCC does not enable ADL in their std::swap
implementation (nor does MSVC, but that's not saying much). So I must be wrong that std::swap
takes on the behavior of boost::swap
, but I don't understand why the change wasn't made. :( And I'm not alone!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您提出概念验证实施方案,我将不得不投票反对。我担心它会破坏下面的代码,我很确定在过去的十几年里我在野外至少见过一两次。
无论您认为上面的代码是好是坏,它都按照作者在 C++98/03 中的意图工作,因此默默地破坏它的门槛相当高。告诉用户在 C++11 中他们不再需要编写
using std::swap;
的好处并不足以抵消默默地将上述代码转变为无限递归的缺点。另一种摆脱
using std::swap;
编写的方法是使用std::iter_swap
代替:I would have had to vote against your proof-of-concept implementation had it been proposed. I fear it would break the following code, which I'm pretty sure I've seen in the wild at least once or twice over the past dozen years.
Whether you think the above is good code or bad, it works as the author intends in C++98/03 and so the bar for silently breaking it is pretty high. Telling users that in C++11 they would no longer have to write
using std::swap;
isn't a sufficiently high benefit to outweigh the disadvantage of silently turning the above code into infinite recursion.Another way to get out of writing
using std::swap;
is to usestd::iter_swap
instead:在 C++20 中,这最终得到标准化:
这使用 ADL 调用正确的重载,并提出在 SFINAE 中使用的正确要求。魔法在 [namespace.std]/7 中指定:
(强调我的)
并且
swap
被指定为 [utility.swap]:(强调我的)
In C++20, this is finally standardized:
This uses ADL to call the correct overload and imposes the correct requirements to use in SFINAE. The magic is specified in [namespace.std]/7:
(emphasis mine)
And
swap
is designated as a customization point in [utility.swap]:(emphasis mine)
这是一个概念验证的实现:
Here's a proof-of-concept implementation:
好吧,
boost::swap()
分派到std::swap()
。要让 std::swap() 执行类似于 boost::swap() 的操作,需要将其委托给其他地方。这在别处是什么?该标准没有强制要求提供实际实现的swap()
的另一个版本。这是可以做到的,但标准没有强制这样做。为什么它不这样做?我没有看到任何建议实施此实施的提案。如果有人希望这样做,我相信它会被提议。
Well,
boost::swap()
dispatches tostd::swap()
. To havestd::swap()
do something similar toboost::swap()
it would need to delegate somewhere else. What is this somewhere else? The standard doesn't mandate another version ofswap()
which provides the actual implementation. This can be done but the standard doesn't mandate it.Why it doesn't do it? I didn't see any proposal proposing this implementation. If someone had wanted this to do done I'm sure it would have been proposed.