1

Using the example for std::swap on cppreference I tried the following SWAP-template:

#include <algorithm>
#include <iostream>

namespace Ns
{
class A
{
    int id{};
 
    friend void swap(A& lhs, A& rhs)
    {
        std::cout << "swap(" << lhs << ", " << rhs << ")\n";
        std::swap(lhs.id, rhs.id);
    }
 
    friend std::ostream& operator<< (std::ostream& os, A const& a)
    {
        return os << "A::id=" << a.id;
    }
 
public:
    A(int i) : id{i} { }
    A(A const&) = delete;
    A& operator = (A const&) = delete;
};
}

template<typename T> void SWAP(T &l, T &r)
{
    try { std::swap(l, r); }
    catch(...) { swap(l, r); }
}

int main()
{
    std::cout << "\n======================\n";
    int a = 5, b = 3;
    std::cout << "before: " << a << ' ' << b << '\n';
    std::swap(a,b);
    std::cout << "after:  " << a << ' ' << b << '\n';
    std::cout << "\n=========\n";
    Ns::A p{6}, q{9};
    std::cout << "before: " << p << ' ' << q << '\n';
    //  std::swap(p, q);  // error, type requirements are not satisfied
    swap(p, q);  // OK, ADL finds the appropriate friend `swap`
    std::cout << "after: " << p << ' ' << q << '\n';

    std::cout << "\n======================\n";
    std::cout << "before: " << a << ' ' << b << '\n';
    SWAP(a,b);
    std::cout << "after:  " << a << ' ' << b << '\n';
    std::cout << "\n=========\n";
    std::cout << "before: " << p << ' ' << q << '\n';
    SWAP(p, q);
    std::cout << "after: " << p << ' ' << q << '\n';
}

to handle the 'friend' swap-function in the namespace; to have just a single SWAP function to call that will handle all cases.

The compiler-error: swap was not declared in this scope

Why does calling swap() for the namespace work in main but not in the template?
and is there a way to have a generalized 'SWAP'-function to handle all such cases?

(edit)

Thanks to @rici the following change to the template works:

template<typename T> void SWAP(T &l, T &r)
{
    using namespace std;
    swap(l, r);
}

I would still appreciate an ELI5 for the first part of my question: what/how/why does this work ...

slashmais
  • 7,069
  • 9
  • 54
  • 80
  • 1
    `try` in C++ does not catch compile-time errors, including failure to resolve a name. Regardless of the `try`, both calls to `swap` must resolve to some function in order for the compile to succeed. It's not like python. – rici Apr 23 '22 at 02:37
  • related/dupe: https://stackoverflow.com/questions/6380862/how-to-provide-a-swap-function-for-my-class – NathanOliver Apr 23 '22 at 02:39
  • @rici: I was thinking that the template instantiation would resolve that? Mistaken? – slashmais Apr 23 '22 at 02:39
  • @NathanOliver: Thx for the link, it answers a lot of questions, except it is still unclear why the call would work in `main' and but not when instantiated via the template? – slashmais Apr 23 '22 at 02:46
  • 1
    I couldn't say without seeing a [mre]. – NathanOliver Apr 23 '22 at 02:55
  • @slashmais: the unqualified call to `swap` can't resolve to `std::swap` because there's no `using namespace std` in scope. You'll need to show more context if you had a different expectation. But that `try` statement is just wrong, regardless. – rici Apr 23 '22 at 02:58
  • 1
    @NathanOliver: I have added my attempt in the question. – slashmais Apr 23 '22 at 03:21
  • @rici: I have implemented your suggestion as shown in my own answer - thx, it works now – slashmais Apr 23 '22 at 03:32

1 Answers1

1

There are 2 problems.

Please first read the definition of 'std::swap' here.

You will read the requirements for the type.

  1. You are using exceptions in your swap function. Remove that.
  2. From the description, you can see that your type must be

Type requirements

  • T must meet the requirements of MoveAssignable and MoveConstructible.
  • T2 must meet the requirements of Swappable.

You defined (deleted) a constructor and assign operator. With that the compiler will not create the standard constructors/assign operators for you. Please read about the rule of 5.

Your class is no longer MoveAssignable and MoveConstructible.

Simply remove the deleted operator and constructor.

Like the below. Then it will compile.

#include <algorithm>
#include <iostream>

namespace Ns
{
    class A
    {
        int id{};

        friend void swap(A& lhs, A& rhs)
        {
            std::cout << "swap(" << lhs << ", " << rhs << ")\n";
            std::swap(lhs.id, rhs.id);
        }

        friend std::ostream& operator<< (std::ostream& os, A const& a)
        {
            return os << "A::id=" << a.id;
        }

    public:
        A(int i) : id{ i } { }
        //A(A const&) = delete;
        //A& operator = (A const&) = delete;
    };
}

template<typename T> 
void SWAP(T& l, T& r)
{
    std::swap(l, r);
}

int main()
{
    std::cout << "\n======================\n";
    int a = 5, b = 3;
    std::cout << "before: " << a << ' ' << b << '\n';
    std::swap(a, b);
    std::cout << "after:  " << a << ' ' << b << '\n';
    std::cout << "\n=========\n";
    Ns::A p{ 6 }, q{ 9 };
    std::cout << "before: " << p << ' ' << q << '\n';
    //  std::swap(p, q);  // error, type requirements are not satisfied
    swap(p, q);  // OK, ADL finds the appropriate friend `swap`
    std::cout << "after: " << p << ' ' << q << '\n';

    std::cout << "\n======================\n";
    std::cout << "before: " << a << ' ' << b << '\n';
    SWAP(a, b);
    std::cout << "after:  " << a << ' ' << b << '\n';
    std::cout << "\n=========\n";
    std::cout << "before: " << p << ' ' << q << '\n';
    SWAP(p, q);
    std::cout << "after: " << p << ' ' << q << '\n';
}
A M
  • 14,694
  • 5
  • 19
  • 44