swap std::unique_ptr with lambda as deleter -- GCC - c++

Can we use a lambda as a deleter with a std::unique_ptr ? Actualy, I did it with clang++ and it was happy to do so.
I'm using std::swap to swap to std::unique_ptr<ObjType, decltyp(deleter)>; where auto deleter = [](struct addrinfo* ptr){if (ptr != nullptr) {freeaddrinfo(ptr);} };. Clang's swap seems to do not need a copy assignment operator, but gcc's std::swap did, as you can see in those 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 says the standard? Can I manage to wap those two std::unique_ptr ? Are they a workaround ? (Maybe encapsulating the lambda inside a std::function? ...)
Edit :
Here is a small example that should be more or less the same thing :
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;
}
The error :
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);
^

This has nothing to do with unique_ptr or tuple, you can reduce the error to this:
int main()
{
auto deleter = []() { };
auto del2 = deleter;
deleter = static_cast<decltype(deleter)>(del2);
}
Which compiles with Clang but fails with G++, giving this 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 last C++11 standard says in [expr.prim.lambda]/19:
The closure type associated with a lambda-expression has a deleted (8.4.3) default constructor and a deleted copy assignment operator. It has an implicitly-declared copy constructor (12.8) and may have an implicitly-declared move constructor (12.8).
So it is up to the compiler whether the type is move-assignable or not.

To expand on Jonathan Wakely's answer:
When you swap to unique_ptrs, you also have to swap their deleters. The problem you are seeing boils down to this: clang can swap two lambdas of the same type, gcc cannot (and the standard allows both as Jonathan quotes it). Demonstration:
#include <utility>
int main() {
auto f = [](){};
auto g(f);
std::swap(f, g);
}
This code works with clang but fails to compile with gcc. (And that is OK.)
That is why it 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;
}
Note that the code became cleaner and more readable as well.
If you absolutely have to solve this with lambdas, then perhaps you could try something hackish like this: Swap only the pointers but not the deleters.
#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 lambdas.

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 operator and the error goes away. I'm guessing gcc doesn't allow move assignment of lambas (I'm using version 4.7.2). Change the lambda to an actual function or functor and you should be alright.

As it turns out, you can solve it with lambdas, as long as they can be converted to function pointers (lambdas capturing nothing).
#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 the struct better. It is likely to be the most efficient one (thanks to inlining), followed by the solution presented here. Passing a heavy-weight std::function looks like an overkill to me if the deleter implementation is really simple. Whether these performance considerations matter, it is the profiler's job to tell.

Related

why does ranges::view_interface<T>::size require a move constructor

I don't understand where the requirement for moving comes from. I can't find it in forward_range and sized_sentinel...
Basic example:
#include <ranges>
#include <string>
#include <iostream>
class vrange: public std::ranges::view_interface<vrange>
{
public:
vrange(std::string &d): data(d){;};
vrange(const vrange &&) = delete;
auto begin() const noexcept { return data.begin(); };
auto end() const noexcept { return data.end(); };
private:
std::string data;
};
int main(){
std::string h("Hello world");
vrange r(h);
std::cout << r.size() << std::endl;
for (const auto &i: r){
std::cout << i;
}
std::cout << std::endl;
}
removing the call to r.size(), or defaulting the vrange move constructor and assignment operator makes it compile fine.
compiler message:
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h: In instantiation of ‘constexpr _Derived& std::ranges::view_interface<_Derived>::_M_derived() [with _Derived = vrange]’:
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h:101:35: required from ‘constexpr bool std::ranges::view_interface<_Derived>::empty() requires forward_range<_Derived> [with _Derived = vrange]’
w.cpp:25:12: required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h:70:23: error: static assertion failed
70 | static_assert(view<_Derived>);
| ^~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h:70:23: note: constraints not satisfied
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/ranges:37:
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:136:13: required for the satisfaction of ‘constructible_from<_Tp, _Tp>’ [with _Tp = vrange]
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:150:13: required for the satisfaction of ‘move_constructible<_Tp>’ [with _Tp = vrange]
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:247:13: required for the satisfaction of ‘movable<_Tp>’ [with _Tp = vrange]
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:137:30: note: the expression ‘is_constructible_v<_Tp, _Args ...> [with _Tp = vrange; _Args = {vrange}]’ evaluated to ‘false’
137 | = destructible<_Tp> && is_constructible_v<_Tp, _Args...>;
This has nothing to do with size specifically.
view_interface is used to build a type that is a view. Well, the ranges::view concept requires that the type is at least moveable. And view_interface has a very specific requirement on the type given as its template argument:
Before any member of the resulting specialization of view_interface other than special member functions is referenced, D shall be complete, and model both derived_from<view_interface<D>> and view.
Well, your type does not model view because it is not moveable. So you broke the rules, and you therefore get undefined behavior. Which can include compile errors happening if you call certain members but not others.

User defined operator conversion resolution order

Is it possible to configure the resolution order of user defined operators? Consider the following code:
#include <memory>
#include <utility>
using P = std::unique_ptr<int>;
struct S{
P p;
operator P() && { return std::move(p); }
operator const P&() const { return p; }
};
S s{std::make_unique<int>()};
P p;
p = std::move(s);
This fails to compile:
In file included from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/memory:76,
from <source>:1:
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/unique_ptr.h:406:19: note: candidate: 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = int; _Dp = std::default_delete<int>]'
406 | unique_ptr& operator=(unique_ptr&&) = default;
| ^~~~~~~~
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/unique_ptr.h:515:19: note: candidate: 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]' (deleted)
515 | unique_ptr& operator=(const unique_ptr&) = delete;
It seems compilation is failing because of an ambiguity between the deleted copy assignment and defaulted move assignment operators.
Why is the compiler unable to resolve the operation here? Is there a way to define the operators to make this work?
If these operators are supposed to be conversion operators, you can define them like this:
operator P() && { return std::move(p); }
operator const P&() & { return p; }
Demo
The second operator needs the lvalue ref-qualifier (the & at the end) because without it, the implicit object parameter can also be an rvalue. That case is also covered with the rvalue ref-qualified version, so the call becomes ambiguous. Same thing if you add the const qualifier, because an rvalue can bind to a const lvalue reference.
You didn't define conversion/cast operators, you defined call operators. As written, you'd need to do:
p = std::move(s)();
to call s.
If you want a user-defined conversion, you'd want to change:
P operator() && { return std::move(p); }
const P& operator() const { return p; }
to something like:
operator P() && { return std::move(p); }
operator P() const { return p; }
though I'm not sure whether that paired overload of a conversion operator is legal.

Boost.TypeErasure: movable functor

Since std::function requires copy semantics and captured lambda cannot be converted to std::function, I try to define movable function using boost.TypeErasure. Everything is OK until move assign operator is required.
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/constructible.hpp>
#include <boost/type_erasure/callable.hpp>
#include <boost/type_erasure/builtin.hpp>
#include <boost/mpl/vector.hpp>
namespace bte = boost::type_erasure;
namespace bm = boost::mpl;
using Foo = bte::any<
bm::vector<
bte::constructible<bte::_self(bte::_self&&)>,
bte::assignable<bte::_self, bte::_self&&>,
bte::destructible<>,
bte::callable<void()>
>
>;
int main()
{
Foo{[&]{}};
}
With gcc, the compile error is:
In file included from /usr/local/include/boost/type_erasure/detail/normalize.hpp:34,
from /usr/local/include/boost/type_erasure/any.hpp:36,
from main.cpp:1:
/usr/local/include/boost/type_erasure/builtin.hpp: In instantiation of 'static void boost::type_erasure::assignable<T, U>::apply(T&, const U&) [with T = main()::<lambda()>; U = main()::<lambda()>&&]':
/usr/local/include/boost/type_erasure/detail/instantiate.hpp:91:9: required from 'static void boost::type_erasure::detail::instantiate_concept4::apply(Concept*, Map*) [with Concept = boost::mpl::vector<boost::type_erasure::constructible<boost::type_erasure::_self(boost::type_erasure::_self&&)>, boost::type_erasure::assignable<boost::type_erasure::_self, boost::type_erasure::_self&&>, boost::type_erasure::destructible<>, boost::type_erasure::callable<void()> >; Map = boost::mpl::map1<boost::mpl::pair<boost::type_erasure::_self, main()::<lambda()> > >]'
/usr/local/include/boost/type_erasure/any.hpp:225:13: required from 'boost::type_erasure::any<Concept, T>::any(U&&) [with U = main()::<lambda()>; Concept = boost::mpl::vector<boost::type_erasure::constructible<boost::type_erasure::_self(boost::type_erasure::_self&&)>, boost::type_erasure::assignable<boost::type_erasure::_self, boost::type_erasure::_self&&>, boost::type_erasure::destructible<>, boost::type_erasure::callable<void()> >; T = boost::type_erasure::_self]'
main.cpp:21:14: required from here
/usr/local/include/boost/type_erasure/builtin.hpp:73:51: error: use of deleted function 'main()::<lambda()>& main()::<lambda()>::operator=(const main()::<lambda()>&)'
static void apply(T& dst, const U& src) { dst = src; }
~~~~^~~~~
main.cpp:21:11: note: a lambda closure type has a deleted copy assignment operator
Foo{[&]{}};
^
I don't understand why apply's argument is const. And what is the correct way?
Coliru link
Update:
I thought lambda is move-assignable. However, it seems wrong.

main()::__lambda1 is not an accessible base of std::_Tuple_impl<1ul, main()::__lambda1>

Take the following:
#include <utility>
#include <functional>
#include <iostream>
struct FooType
{
FooType(std::tuple<std::function<void()>, std::function<void()>> t)
{
std::get<0>(t)();
std::get<1>(t)();
}
};
int main()
{
FooType foo(
std::make_tuple(
[]() { std::cout << "a\n"; },
[]() { std::cout << "b\n"; }
)
);
}
It builds and runs fine with GCC 4.9 and higher, but with GCC 4.8 (which is my build system) the following compiler error results:
In file included from main.cpp:2:0:
/usr/include/c++/4.8/functional: In substitution of 'template<class _Res, class ... _ArgTypes> template<class _Functor> using _Invoke = decltype (std::__callable_functor(declval<_Functor&>())((declval<_ArgTypes>)()...)) [with _Functor = std::_Tuple_impl<1ul, main()::__lambda1>; _Res = void; _ArgTypes = {}]':
/usr/include/c++/4.8/functional:2257:9: required from 'constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(std::_Tuple_impl<_Idx, _UHead, _UTails ...>&&) [with _UHead = main()::__lambda0; _UTails = {main()::__lambda1}; long unsigned int _Idx = 0ul; _Head = std::function<void()>; _Tail = {std::function<void()>}]'
/usr/include/c++/4.8/tuple:556:60: required from 'constexpr std::tuple<_T1, _T2>::tuple(std::tuple<_U1, _U2>&&) [with _U1 = main()::__lambda0; _U2 = main()::__lambda1; <template-parameter-2-3> = void; _T1 = std::function<void()>; _T2 = std::function<void()>]'
main.cpp:21:5: required from here
/usr/include/c++/4.8/functional:2181:71: error: 'main()::__lambda1' is not an accessible base of 'std::_Tuple_impl<1ul, main()::__lambda1>'
using _Invoke = decltype(__callable_functor(std::declval<_Functor&>())
^
Presumably I've hit a "bug" in GCC 4.8's experimental C++11 implementation, but I can't find any discussion about it online. Is this issue familiar to anyone? Is there a cheap workaround?
Passing std::function in tuples is core to the design I've just created — shame on me for building it in Coliru without switching to GCC 4.8 specifically until the end!
Creating a tuple with explicit template arguments works as a workaround:
#include <utility>
#include <functional>
#include <iostream>
struct FooType
{
FooType(std::tuple<std::function<void()>, std::function<void()>> t)
{
std::get<0>(t)();
std::get<1>(t)();
}
};
int main()
{
FooType foo(
std::make_tuple
<
std::function<void()>,
std::function<void()>
>(
[]() { std::cout << "a\n"; },
[]() { std::cout << "b\n"; }
)
);
}

(std::__cxx11::string) [with T = std::__cxx11::basic_string<char>; std::__cxx11::string = std::__cxx11::basic_string<char>] cannot be overloaded

C++ showing error with constructor overloading in templates
(std::__cxx11::string) [with T = std::__cxx11::basic_string<char>; std::__cxx11::string = std::__cxx11::basic_string<char>]cannot be overloaded
I am trying to overload a Template class constructor here.
Extended error:
In file included from main.cpp:2:0:
dlist.h: In instantiation of class Sinwan::DList::DoublyLinkList<std::__cxx11::basic_string<char> >:
main.cpp:5:45: required from here
dlist.h:62:13: error:Sinwan::DList::DoublyLinkList<T>::DoublyLinkList(std::__cxx11::string) [with T = std::__cxx11::basic_string<char>; std::__cxx11::string = std::__cxx11::basic_string<char>] cannot be overloaded
DoublyLinkList(std::string dummyData_)
^
dlist.h:52:13: error: with Sinwan::DList::DoublyLinkList<T>::DoublyLinkList(T) [with T = std::__cxx11::basic_string<char>]
DoublyLinkList(T dummyData_)
code inside my .h file:
DoublyLinkList(T dummyData_)
{
node = new Node;//dummy node
node->_next=NULL;
node->_prev=NULL;
node->_data=dummyData_;
head=node;
tail=node;
_iteratorObj=begin();
}
DoublyLinkList(std::string dummyData_)
{
node = new Node;//dummy node
node->_next=NULL;
node->_prev=NULL;
node->_data=dummyData_;
head=node;
tail=node;
_iteratorObj=begin();
}
Main.cpp:
#include<iostream>
#include "dlist.h"
int main()
{
Sinwan::DList::DoublyLinkList<std::string>::Iterator it;
Sinwan::DList::DoublyLinkList<std::string> listObj("0");
}
If I change std::string to int, it works fine. Can anybody help me to point out the prblm??
We still don't have a Minimal, Complete, and Verifiable Example, but at least it's possible to guess about the problem.
Assuming that T is a class template argument, then if you have DoublyLinkList<std::string> then the type T will be std::string, and you will have two constructors taking std::string as argument. You can't have that, if you overload a function (constructor or other) it must have different signatures.
You should probably not have the constructor taking an explicit std::string argument.