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

5

6
#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;
}

above is a test sample. I find if I use swap(array[1], array[4]);, it also swaps the values of two positions in array. But this confuses me, because the function swap() needs two pointers, not two integer values.

Thanks for your help:)

Gore answered 24/1, 2013 at 19:41 Comment(6)
Aside: Never say endl when you mean '\n'. They are practically equivalent, but the use of the former can slow your program dramatically.Cleaves
@Robᵩ In what use case? I am interested...Ytterbium
@Ytterbium endl will flush the buffer, whereas \n will fill the buffer first. when writing a lot of stuff, you notice the difference.Invagination
When writing a lot of stuff to a buffered stream. Try writing a million single-digit numbers to a disk file, one line at a time. std::endl will go slower.Cleaves
Please note that your swap will fail if you try to swap a variable with itself. For example, int i = 42; swap(&i, &i); will set i to 0, which is wrong. The funny xor trick can be used for stupid interviews, maybe. It has absolutely no value in the real world.Geri
@FredOverflow, would you provide another answer to solve this problem with no temporary variable?Gore
G
16
using namespace std;  

This is your culprit. When you import the std:: namespace, you get every identifier declared in that namespace, potentially including std::swap.

Thus you are invoking std::swap<int>(int&,int&) (from the standard library) and not ::swap(int*,int*) (from your program.)

The moral of the story: never say using namespace std;. It's just too big.

Glyptic answered 24/1, 2013 at 19:43 Comment(1)
...and this is why using namespace std; is often a bad idea, even though nearly every introductory tutorial tells you to use it because they're too lazy :)Awed
P
7

It doesn't use your swap, but std::swap.

Try calling it as ::swap(array[1], array[4]); and you'll get an error.

This is why using namespace std; is bad.

Politicking answered 24/1, 2013 at 19:43 Comment(0)
O
5

This is why you should avoid using namespace std;.

Including a standard header has apparently dragged a declaration of std::swap into your program; and using namespace std; has dumped it into the global namespace. So your code is calling that, not your version.

Outcome answered 24/1, 2013 at 19:44 Comment(0)
G
5

Would you provide another answer to solve this problem with no temporary variable?

I don't think it can be done. Where does the requirement "no temporary variable" come from, anyway? Do you think temporary variables make your code slower? Let us examine if this is the case here.

Exhibit A: Some hack. Not immediately obvious how it works. Cannot swap a variable with itself correctly:

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

Generated assembly code:

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

Exhibit B: Straight-forward code with a temporary variable. It can swap a variable with itself correctly:

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

Generated assembly code:

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

The temporary variable solution is easier to understand, handles all cases and results in faster code.

Again, outside of interview situations, the xor trick is completely useless. And if I were the interviewer and the candidate knew the xor trick but did not qualify it by saying "This is a cute trick, but I would never use this in the real world", I sure as hell wouldn't hire him. Let me finish this answer with a quote:

Clarity trumps cleverness every time. Larry Osterman

Geri answered 27/1, 2013 at 8:44 Comment(0)
H
-1

Your problem has nothing to do with using namespace std;. The problem is that you're passing an lvalue reference to an int (int&) to a function that's overloaded on int& and int*. If you removed the using directive, you would not be calling your own swap; in fact, your code wouldn't compile, since C++ does not allow implicit conversion from pointers to int. In order to use your function, you would have to call it as swap(&array[1], &array[4]);

Now, if you corrected your function call to reflect this, it should work, regardless of the presence of the using directive. I would, however, advise against doing so; the semantics of std::swap are to swap two references, not pointers. Not only is overloading standard functions with incompatible semantics confusing to those familiar with the standard library, it may very well break perfectly valid code that uses std::swap on int*.

You may be thinking, "But that will only happen if they're using namespace std;, which they should never do." Believe it or not, however, swap is exactly the sort of application where you do want that using directive (properly scoped, of course). The reason for this is that user code may do exactly what you're trying to do: specialize the swap algorithm. If some library code simply said std::swap, any user-defined overload would be ignored, making that overload useless. If, instead, your code looked like

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

the compiler would be able to employ ADL correctly and choose user-defined swap for any given T (say, int), while still being able to use std::swap for other T.

In other words: always give swap reference parameters, and use a scoped using declaration whenever you need to swap templated types.

Hallam answered 25/1, 2013 at 3:56 Comment(7)
If you removed the using directive, you would not be calling your own swap What?! And what does ADL have to do with it?! The arguments are int.Overmatter
@Non-Stop Time Travel: Yes, the actual arguments are int. The parameters to the function as defined in the question, however, are int*. Passing int to a function expecting int* does not work.Hallam
But there is a swap in std that can take int&. You completely sidestepped everything I asked you in my previous comment.Overmatter
"But there is a swap in std that can take int&" -- And by removing the using directive, you make that definition of swap inaccessible to swap(array[1], array[4]);. The user-defined swap does not qualify for that call because its arguments are int*. The bit about ADL has to do with the behaviour of code that overloads swap correctly.Hallam
Exactly, so "if you removed the using directive, you would" only be able to call "your own swap", which is the opposite of what you said.Overmatter
You would not even be able to call it, because int& is not implicitly convertible to int*.Hallam
No, just those calls already in main would fail. By writing different calls, you'd be able to call it.Overmatter

© 2022 - 2024 — McMap. All rights reserved.