Search code examples
c++priority-queuedeleted-functions

Attempting to reference a deleted function error when swapping priority queues


I was just trying to swap the data between the two priority queues and got this error. I also did some googling and still don't know what's wrong here.

#include <queue>

class Node
{
public:
    int idx;
};
auto greater = []( const Node& a, const Node&  b) {return a.idx > b.idx; };
using QUEUE  = std::priority_queue<Node, std::vector<Node>, decltype(greater)>;

void foo(QUEUE& a)
{
    QUEUE b(greater);
    a.swap(b);
}

int main()
{

}

Here's the full error message:

Error C2280 ' < lambda_07efac20ebfa61cc8bb35aebd7d81f7c> &<< lambda_07efac20ebfa61cc8bb35aebd7d81f7c>>::operator =(const << lambda_07efac20ebfa61cc8bb35aebd7d81f7c>> &)': attempting to reference a deleted function


Solution

  • The implementation of std::swap in stdlibc++ (and, most likely, standard library for Visual Studio) uses old-fashioned assignments:

    swap(_Tp& __a, _Tp& __b)
    {
        // concept requirements
        __glibcxx_function_requires(_SGIAssignableConcept<_Tp>)
    
        _Tp __tmp = __a;
        __a = __b;
        __b = __tmp;
    }
    

    On the contrary, libcxx implements it using std::move:

    swap(_Tp& __x, _Tp& __y) _NOEXCEPT_(is_nothrow_move_constructible<_Tp>::value &&
                                        is_nothrow_move_assignable<_Tp>::value)
    {
         _Tp __t(_VSTD::move(__x));
         __x = _VSTD::move(__y);
         __y = _VSTD::move(__t);
    }
    

    In order to swap objects of type std::priority_queue you have to swap their comparators, which are represented as a lambda in your code. Swapping lambda in stdlibc++ is impossible due to deleted copy assignment operator.

    By the way, there is no need to use a lambda since your capture list is empty. A simple function will do the job.

    bool greater(const Node& a, const Node&  b) {return a.idx > b.idx;};
    using QUEUE = std::priority_queue<Node, std::vector<Node>, decltype(&greater)>;
    
    void foo(QUEUE&& a)
    {
        QUEUE b(&greater);
        a.swap(b);
    }
    

    Alternatively, you can replace a lambda with a callable object which has move assignment.