Можете ли вы поменять std :: queue с лямбда-компаратором?

Я пытаюсь очистить очередь std ::, используя пример в https://stackoverflow.com/a/709161/837451 через своп. Однако он не работает с лямбда-компаратором из-за ошибки «удаленная функция».

минимальная за работой неудачный пример:

#include <queue>
#include <vector>
using namespace std;
int main(){
typedef pair<int,float> ifpair;
auto comp = []( ifpair a,  ifpair b ) { return a.second > b.second; };
typedef priority_queue< ifpair , vector<ifpair>, decltype( comp ) > t_npq;
t_npq npq( comp );
//do something with npq. finish using it (without emptying it) and clear for next round
t_npq empty( comp );
swap(npq , empty);
}

Компилировать с

g++ -std=c++11 /tmp/test.cpp -o /tmp/o

И я получаю следующую ошибку:

/usr/include/c++/4.8/bits/move.h:176:11: error: use of deleted function ‘main()::__lambda0& main()::__lambda0::operator=(const main()::__lambda0&)’
__a = _GLIBCXX_MOVE(__b);
^
/tmp/test.cpp:6:18: note: a lambda closure type has a deleted copy assignment operator

g ++ -v

Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.8.1-10ubuntu9' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu9)

Мне любопытно, что именно здесь происходит, но, что важнее, мне бы очень хотелось узнать, как сделать эту работу.

6

Решение

Хотя результатом лямбда-выражения является перемещение, оно не обязательно должно быть назначаемым и, конечно, не копируемым. Я бы просто обойти проблему с помощью std::reference_wrapper<decltype(comp)> для объекта сравнения:

typedef pair<int,float> ifpair;
auto comp = []( ifpair a,  ifpair b ) { return a.second > b.second; };
typedef priority_queue< ifpair , vector<ifpair>,
std::reference_wrapper<decltype( comp ) >> t_npq;
t_npq npq( std::ref(comp) );
t_npq empty( std::ref(comp) );
swap(npq , empty);

Поскольку полная информация о типе лямбда-выражения хранится в ссылочной обертке, это должно работать, даже если замыкание не пустое и, где это возможно, должна быть возможность встроить функцию.

8

Другие решения

Вы пытались использовать std::function?

#include <queue>
#include <vector>
#include <functional>
using namespace std;
int main(){
typedef pair<int,float> ifpair;
std::function< bool ( ifpair, ifpair )> comp = []( ifpair a,  ifpair b ) { return a.second > b.second; };
typedef priority_queue< ifpair , vector<ifpair>, decltype( comp ) > t_npq;
t_npq npq( comp );
//do something with npq. finish using it (without emptying it) and clear for next round
t_npq empty( comp );
swap(npq , empty);
}
1

Лямбды не назначаются — 5.1.2 / 19:

Тип замыкания, связанный с лямбда-выражением, имеет удаленный конструктор по умолчанию и оператор присваивания удаленной копии.

Обмен контейнеров тоже хочет назначить компараторы, так что это не работает.

Однако вы можете легко заставить его работать, сначала преобразовав лямбду без сохранения состояния в указатель на функцию:

bool (*p)(ifpair, ifpair) = [](ifpair a, ifpair b) { return a.second > b.second; };

Теперь используйте:

priority_queue<ifpair, vector<ifpair>, bool(*)(ifpair, ifpair)>

(Возможно, вы захотите ввести typedef для типа функции: using comp_type = bool(iftype, iftype), а затем использовать comp_type * везде.)

1

Как указывает ошибка компиляции, лямбда-объекты не могут быть назначены. Вы можете использовать другой тип функтора для очереди, но все равно записать его как labmda:

  1. использование std::function<bool(ifpair,ifpair)>: http://ideone.com/HZywoV

    Но это добавляет (вероятно, заметно) накладные расходы из-за некоторых косвенных указаний при реализации std::function но я думаю, это сильно зависит от реализации стандартной библиотеки и оптимизации компилятора. Может быть, самое лучшее решение относительно того, как выглядит код.

  2. Используйте указатель на функцию bool(*)(ifpair,ifpair): http://ideone.com/ZhFq3C

    Это не должно страдать от каких-либо накладных расходов по сравнению с std::function но к вашему текущему решению, поскольку могут быть сделаны некоторые оптимизации компилятора для вашего лямбда-кода, которые в таком случае невозможны (то есть встраивают его в остальную часть std::queue код, который, например, исключает копирование двух пар). Использование указателей на функции выглядит довольно старомодно.

  3. Используйте пользовательский класс функторов, который может быть простым: http://ideone.com/9pcQFc

    template<typename Pair>
    struct GreaterBySecond {
    bool operator()(Pair a, Pair b) const {
    return a.second > b.second;
    }
    };
    

    Это должно устранить все накладные расходы, обсужденные выше. Я бы предпочел это, если производительность имеет значение.

1
По вопросам рекламы [email protected]