Swap std :: unique_ptr with lambda as deleter - GCC

Is it possible to use lambda as a deleter with std :: unique_ptr? In fact, I did this with clang ++, and he was happy to do it.

I use std::swap to go to std::unique_ptr<ObjType, decltyp(deleter)>; where auto deleter = [](struct addrinfo* ptr){if (ptr != nullptr) {freeaddrinfo(ptr);} }; . Clang swap does not seem to need a copy assignment operator, but gcc std :: swap did as you can see in these logs:

 In file included from /usr/include/c++/4.8.1/memory:81:0, from /home/zenol/proj/src/PROJ/TCPClient.cpp:28: /usr/include/c++/4.8.1/bits/unique_ptr.h: In instantiation of 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = addrinfo; _Dp = Proj::TCPClient::connect(const Proj::SocketAddress&, int)::__lambda0]': /usr/include/c++/4.8.1/bits/move.h:176:11: required from 'void std::swap(_Tp&, _Tp&) [with _Tp = std::unique_ptr<addrinfo, Proj::TCPClient::connect(const Proj::SocketAddress&, int)::__lambda0>]' /home/zenol/proj/src/Proj/SocketHelp.hpp:109:50: required from 'void Proj::retrieve_addresses(std::string, int, addrinfo&, addrinfo*&, T&, U) [with T = std::unique_ptr<addrinfo, Proj::TCPClient::connect(const Proj::SocketAddress&, int)::__lambda0>; U = Proj::TCPClient::connect(const Proj::SocketAddress&, int)::__lambda0; std::string = std::basic_string<char>]' /home/zenol/proj/src/PROJ/TCPClient.cpp:65:49: required from here /usr/include/c++/4.8.1/bits/unique_ptr.h:193:16: erreur: use of deleted function 'Proj::TCPClient::connect(const Proj::SocketAddress&, int)::__lambda0& Proj::TCPClient::connect(const Proj::SocketAddress&, int)::__lambda0::operator=(const Proj::TCPClient::connect(const Proj::SocketAddress&, int)::__lambda0&)' get_deleter() = std::forward<deleter_type>(__u.get_deleter()); ^ /home/zenol/proj/src/Proj/TCPClient.cpp:56:21: note: a lambda closure type has a deleted copy assignment operator auto deleter = [](struct addrinfo* ptr) ^ 

What does the standard say? Can I use these two std :: unique_ptr? Is this a workaround? (Maybe lambda encapsulation inside the function std :: ??)

Edit: Here is a small example that should be more or less the same:

 auto deleter = [](struct addrinfo* ptr) {if (ptr != nullptr) {freeaddrinfo(ptr);} }; std::unique_ptr<struct addrinfo, decltype(deleter)> resources_keeper(nullptr, deleter); int main() { decltype(resources_keeper) plouf1(nullptr, deleter); decltype(resources_keeper) plouf2(nullptr, deleter); std::swap(plouf1, plouf2); return 0; } 

Mistake:

 In file included from /usr/include/c++/4.8.1/bits/stl_pair.h:59:0, from /usr/include/c++/4.8.1/bits/stl_algobase.h:64, from /usr/include/c++/4.8.1/memory:62, from mini.cpp:1: /usr/include/c++/4.8.1/bits/move.h: In instantiation of 'void std::swap(_Tp&, _Tp&) [with _Tp = __lambda0]': /usr/include/c++/4.8.1/tuple:381:36: required from 'void std::_Tuple_impl<_Idx, _Head, _Tail ...>::_M_swap(std::_Tuple_impl<_Idx, _Head, _Tail ...>&) [with long unsigned int _Idx = 1ul; _Head = __lambda0; _Tail = {}]' /usr/include/c++/4.8.1/tuple:382:35: required from 'void std::_Tuple_impl<_Idx, _Head, _Tail ...>::_M_swap(std::_Tuple_impl<_Idx, _Head, _Tail ...>&) [with long unsigned int _Idx = 0ul; _Head = addrinfo*; _Tail = {__lambda0}]' /usr/include/c++/4.8.1/tuple:667:33: required from 'void std::tuple<_T1, _T2>::swap(std::tuple<_T1, _T2>&) [with _T1 = addrinfo*; _T2 = __lambda0]' /usr/include/c++/4.8.1/tuple:1050:7: required from 'void std::swap(std::tuple<_Elements ...>&, std::tuple<_Elements ...>&) [with _Elements = {addrinfo*, __lambda0}]' /usr/include/c++/4.8.1/bits/unique_ptr.h:269:21: required from 'void std::unique_ptr<_Tp, _Dp>::swap(std::unique_ptr<_Tp, _Dp>&) [with _Tp = addrinfo; _Dp = __lambda0]' /usr/include/c++/4.8.1/bits/unique_ptr.h:484:7: required from 'void std::swap(std::unique_ptr<_Tp, _Dp>&, std::unique_ptr<_Tp, _Dp>&) [with _Tp = addrinfo; _Dp = __lambda0]' mini.cpp:21:29: required from here /usr/include/c++/4.8.1/bits/move.h:176:11: erreur: use of deleted function '__lambda0& __lambda0::operator=(const __lambda0&)' __a = _GLIBCXX_MOVE(__b); ^ mini.cpp:9:17: note: a lambda closure type has a deleted copy assignment operator auto deleter = [](struct addrinfo* ptr) ^ In file included from /usr/include/c++/4.8.1/bits/stl_pair.h:59:0, from /usr/include/c++/4.8.1/bits/stl_algobase.h:64, from /usr/include/c++/4.8.1/memory:62, from mini.cpp:1: /usr/include/c++/4.8.1/bits/move.h:177:11: erreur: use of deleted function '__lambda0& __lambda0::operator=(const __lambda0&)' __b = _GLIBCXX_MOVE(__tmp); ^ 
+8
c ++ gcc lambda c ++ 11 unique-ptr
source share
4 answers

To deploy Jonathan Wackley, answer :

When switching to unique_ptr s, you will also have to swap your hits. The problem you see boils down to this: clang can change two lambdas of the same type, gcc cannot (and the standard allows, as Jonathan quotes). Demonstration:

 #include <utility> int main() { auto f = [](){}; auto g(f); std::swap(f, g); } 

This code works with clang, but not compiled with gcc. (And this is normal.)

That is why this is happening.


I suggest the following:

 #include <memory> #include <utility> struct addrinfo { }; void freeaddrinfo(addrinfo* ) { } struct deleter { void operator()(struct addrinfo* ptr) { if (ptr != nullptr) freeaddrinfo(ptr); } }; using resources_keeper = std::unique_ptr<struct addrinfo, deleter>; int main() { resources_keeper plouf1(nullptr); resources_keeper plouf2(nullptr); std::swap(plouf1, plouf2); return 0; } 

Please note that the code has become cleaner and more readable.


If you absolutely need to solve this problem with lambdas, perhaps you can try something hackish like this: exchange only pointers, but not delete.

 #include <iostream> #include <memory> #include <utility> using namespace std; template <class T, class D> void swap_pointers_but_not_deleters(unique_ptr<T,D>& x, unique_ptr<T,D>& y) noexcept { T* x_ptr = x.release(); x.reset(y.release()); y.reset(x_ptr); } int main() { auto deleter = [](int* p){ delete p; }; unique_ptr<int,decltype(deleter)> a(new int(1),deleter); unique_ptr<int,decltype(deleter)> b(new int(2),deleter); swap_pointers_but_not_deleters(a, b); cout << "a = " << *a << ", b = " << *b << endl; } 

Although this code seems to work, I really don't like it. I suggest the first solution that does not use lambda.

+3
source share

This has nothing to do with unique_ptr or tuple , you can reduce this error:

 int main() { auto deleter = []() { }; auto del2 = deleter; deleter = static_cast<decltype(deleter)>(del2); } 

Which compiles with Clang, but not with g ++, which gives the following error:

 t.cc: In function 'int main()': t.cc:5:11: error: use of deleted function 'main()::<lambda()>& main()::<lambda()>::operator=(const main()::<lambda()>&)' deleter = static_cast<decltype(deleter)>(del2); ^ t.cc:3:19: note: a lambda closure type has a deleted copy assignment operator auto deleter = []() { }; ^ 

The latest C ++ 11 standard says in [expr.prim.lambda] / 19:

The closure type associated with the lambda expression has a remote (8.4.3) default constructor and a remote copy delete operator. It has an implicitly declared copy constructor (12.8) and may have an implicitly declared move constructor (12.8).

Thus, the compiler depends on whether the type is assignable to move or not.

+5
source share

I can reproduce a similar error with the following code:

 struct A { A() = default; A(A&&) = default; //A & operator=(A&&) = default; A(A const & ) = delete; }; int main() { A a, b; std::swap(a,b); } 

Uncomment the move assignment statement and the error disappears. I assume gcc does not allow lambas redirects (I am using version 4.7.2). Change the lambda to the actual function or functor, and you should be fine.

+2
source share

As it turns out, you can solve this with lambdas if they can be converted to function pointers (lambdas doesn't commit anything).

 #include <memory> #include <utility> struct addrinfo { }; void freeaddrinfo(addrinfo* ) { } auto deleter = [](struct addrinfo* ptr) { if (ptr != nullptr) freeaddrinfo(ptr); }; using resources_keeper = std::unique_ptr<struct addrinfo, void(*)(struct addrinfo*)>; int main() { resources_keeper plouf1(nullptr,deleter); resources_keeper plouf2(nullptr,deleter); std::swap(plouf1, plouf2); return 0; } 

However, I still like my other solution with structure. This is likely to be the most efficient (thanks to embedding) and then the solution presented here. Passing the heavy weight of std::function seems redundant to me if the deleter implementation is really simple. Regardless of whether these performance considerations apply, the profiler job should be specified.

+1
source share

All Articles