为什么 swap() 在我不用两个指针调用它时可以很好地工作?

why swap() can work well when I don't call it with two pointer?

提问人:city 提问时间:1/25/2013 最后编辑:Yangcity 更新时间:10/27/2013 访问量:321

问:

#include <iostream>

using namespace std;

void swap(int *a, int *b) {
    *a = *a^*b;
    *b = *a^*b;
    *a = *a^*b;
}

int main()
{
    int array[]={1,9,2,8,3,7};
    for(int i=0; i<6; i++)
        cout<<array[i];
    cout<<endl;
    swap(array[1], array[4]);
    for(int i=0; i<6;i++)
        cout<<array[i];
    cout<<endl;
    return 0;
}

以上是测试样品。我发现如果我使用 ,它也会交换数组中两个位置的值。但这让我感到困惑,因为该函数需要两个指针,而不是两个整数值。swap(array[1], array[4]);swap()

感谢您的帮助:)

C++ 命名空间 交换 using-directives argument-dependent-lookup

评论

0赞 Robᵩ 1/25/2013
旁白:永远不要说你的意思.它们实际上是等价的,但使用前者会大大减慢您的程序速度。endl'\n'
0赞 PinkElephantsOnParade 1/25/2013
@Rob φ 在什么用例中?我很感兴趣...
1赞 TemplateRex 1/25/2013
@PinkElephantsOnParade 将刷新缓冲区,而将首先填充缓冲区。在写很多东西时,你会注意到其中的差异。endl\n
0赞 Robᵩ 1/25/2013
将大量内容写入缓冲流时。尝试将一百万个位数写入磁盘文件,一次一行。 会走得更慢。std::endl
1赞 fredoverflow 1/25/2013
请注意,如果您尝试将变量与自身交换,则交换将失败。例如,将设置为 0,这是错误的。有趣的 xor 技巧可以用于愚蠢的采访,也许。它在现实世界中绝对没有价值。int i = 42; swap(&i, &i);i

答:

7赞 Luchian Grigore 1/25/2013 #1

它不使用你的 ,而是 .swapstd::swap

尝试将其称为 as,您将收到错误。::swap(array[1], array[4]);

这就是为什么不好。using namespace std;

16赞 Robᵩ 1/25/2013 #2
using namespace std;  

这是你的罪魁祸首。导入命名空间时,将获得该命名空间中声明的每个标识符,可能包括 .std::std::swap

因此,您是在(从标准库)而不是(从您的程序)调用。std::swap<int>(int&,int&)::swap(int*,int*)

故事的寓意是:永远不要说。它太大了。using namespace std;

评论

3赞 us2012 1/25/2013
...这就是为什么这通常是一个坏主意,尽管几乎每个入门教程都告诉你使用它,因为他们太懒了:)using namespace std;
5赞 Mike Seymour 1/25/2013 #3

这就是为什么你应该避免.using namespace std;

包含标准标头显然已将声明拖入程序中;并已将其转储到全局命名空间中。所以你的代码是在调用它,而不是你的版本。std::swapusing namespace std;

-1赞 Stuart Olsen 1/25/2013 #4

您的问题与 无关。问题在于,您正在将对 int () 的左值引用传递给在 和 上重载的函数。如果删除了 using 指令,则不会调用自己的 swap;事实上,您的代码无法编译,因为 C++ 不允许从指针到 int 的隐式转换。为了使用您的函数,您必须将其称为using namespace std;int&int&int*swap(&array[1], &array[4]);

现在,如果您更正了函数调用以反映这一点,那么无论是否存在 using 指令,它都应该可以工作。但是,我建议不要这样做;的语义是交换两个引用,而不是指针。重载具有不兼容语义的标准函数不仅会让熟悉标准库的人感到困惑,而且很可能会破坏使用 on 的完全有效的代码。std::swapstd::swapint*

你可能会想,“但这只有在他们这样做的情况下才会发生,而他们永远不应该这样做。然而,信不信由你,正是那种你确实想要使用指令的应用程序(当然,范围是正确的)。这样做的原因是,用户代码可能完全可以执行您要执行的操作:使算法专用化。如果一些库代码简单地说,任何用户定义的重载都会被忽略,使该重载变得毫无用处。相反,如果你的代码看起来像using namespace std;swapswapstd::swap

template <typename T>
void foo (T& a, T& b)
{
    using std::swap; // Or simply namespace std;
    // ...
    swap (a, b);
    // ...
}

编译器将能够正确使用 ADL 并为任何给定的 T(例如,)选择用户定义的交换,同时仍然能够用于其他 T。intstd::swap

换言之:始终提供引用参数,并在需要交换模板化类型时使用作用域化 using 声明。swap

评论

0赞 Lightness Races in Orbit 1/27/2013
If you removed the using directive, you would not be calling your own swap什么?!ADL与它有什么关系?!参数是 .int
0赞 Stuart Olsen 1/29/2013
@Non停止时间旅行:是的,实际参数是 .但是,问题中定义的函数的参数是 。传递给函数 expecting 不起作用。intint*intint*
0赞 Lightness Races in Orbit 1/29/2013
但是有一个可以采取.你完全回避了我在之前的评论中问你的一切。swapstdint&
0赞 Stuart Olsen 1/30/2013
“但是有一个可以接受的” -- 通过删除指令,你使 .用户定义的不符合该调用的条件,因为它的参数是 。关于 ADL 的一点与正确重载的代码的行为有关。swapstdint&usingswapswap(array[1], array[4]);swapint*swap
0赞 Lightness Races in Orbit 1/30/2013
没错,所以“如果你删除了 using 指令,你将”只能调用“你自己的交换”,这与你所说的相反。
5赞 fredoverflow 1/27/2013 #5

你会提供另一个答案来解决这个问题,没有临时变量吗?

我认为这是不可能做到的。无论如何,“没有临时变量”的要求从何而来?你认为临时变量会让你的代码变慢吗?让我们看看这里是否是这种情况。

图表 A:一些黑客。不是很明显它是如何工作的。无法正确地将变量与自身交换:

void swap1(int* a, int* b)
{
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}

生成的汇编代码:

movl    (%rsi), %eax
xorl    (%rdi), %eax
movl    %eax, (%rdi)
xorl    (%rsi), %eax
movl    %eax, (%rsi)
xorl    %eax, (%rdi)

图表 B:带有临时变量的简单代码。它可以正确地将变量与自身交换:

void swap2(int* a, int* b)
{
    int c = *a;
    *a = *b;
    *b = c;
}

生成的汇编代码:

movl    (%rdi), %eax
movl    (%rsi), %edx
movl    %edx, (%rdi)
movl    %eax, (%rsi)

临时变量解决方案更易于理解,可以处理所有情况,并导致代码速度更快。

同样,在面试情况之外,xor 技巧是完全没用的。如果我是面试官,而候选人知道 xor 技巧,但没有通过说“这是一个可爱的技巧,但我永远不会在现实世界中使用它”来限定它,我肯定不会雇用他。让我用一句话来结束这个答案:

每次都胜过聪明。拉里·奥斯特曼