How to overload std::swap()

后端 未结 4 1926
迷失自我
迷失自我 2020-11-22 15:00

std::swap() is used by many std containers (such as std::list and std::vector) during sorting and even assignment.

But the std

4条回答
  •  难免孤独
    2020-11-22 15:45

    Attention Mozza314

    Here is a simulation of the effects of a generic std::algorithm calling std::swap, and having the user provide their swap in namespace std. As this is an experiment, this simulation uses namespace exp instead of namespace std.

    // simulate 
    
    #include 
    
    namespace exp
    {
    
        template 
        void
        swap(T& x, T& y)
        {
            printf("generic exp::swap\n");
            T tmp = x;
            x = y;
            y = tmp;
        }
    
        template 
        void algorithm(T* begin, T* end)
        {
            if (end-begin >= 2)
                exp::swap(begin[0], begin[1]);
        }
    
    }
    
    // simulate user code which includes 
    
    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);
    }
    

    For me this prints out:

    generic exp::swap
    

    If your compiler prints out something different then it is not correctly implementing "two-phase lookup" for templates.

    If your compiler is conforming (to any of C++98/03/11), then it will give the same output I show. And in that case exactly what you fear will happen, does happen. And putting your swap into namespace std (exp) did not stop it from happening.

    Dave and I are both committee members and have been working this area of the standard for a decade (and not always in agreement with each other). But this issue has been settled for a long time, and we both agree on how it has been settled. Disregard Dave's expert opinion/answer in this area at your own peril.

    This issue came to light after C++98 was published. Starting about 2001 Dave and I began to work this area. And this is the modern solution:

    // simulate 
    
    #include 
    
    namespace exp
    {
    
        template 
        void
        swap(T& x, T& y)
        {
            printf("generic exp::swap\n");
            T tmp = x;
            x = y;
            y = tmp;
        }
    
        template 
        void algorithm(T* begin, T* end)
        {
            if (end-begin >= 2)
                swap(begin[0], begin[1]);
        }
    
    }
    
    // simulate user code which includes 
    
    struct A
    {
    };
    
    void swap(A&, A&)
    {
        printf("swap(A, A)\n");
    }
    
    // exercise simulation
    
    int main()
    {
        A a[2];
        exp::algorithm(a, a+2);
    }
    

    Output is:

    swap(A, A)
    

    Update

    An observation has been made that:

    namespace exp
    {    
        template <>
        void swap(A&, A&)
        {
            printf("exp::swap(A, A)\n");
        }
    
    }
    

    works! So why not use that?

    Consider the case that your A is a class template:

    // simulate user code which includes 
    
    template 
    struct A
    {
    };
    
    namespace exp
    {
    
        template 
        void swap(A&, A&)
        {
            printf("exp::swap(A, A)\n");
        }
    
    }
    
    // exercise simulation
    
    int main()
    {
        A a[2];
        exp::algorithm(a, a+2);
    }
    

    Now it doesn't work again. :-(

    So you could put swap in namespace std and have it work. But you'll need to remember to put swap in A's namespace for the case when you have a template: A. And since both cases will work if you put swap in A's namespace, it is just easier to remember (and to teach others) to just do it that one way.

提交回复
热议问题