将std::unique_ptr与lambda交换为deleter--GCC
swap std::unique_ptr with lambda as deleter -- GCC
我们可以使用lambda作为std::unique_ptr的deleter吗?事实上,我用叮当++做了这件事,它很高兴这样做。
我正在使用std::swap
交换到std::unique_ptr<ObjType, decltyp(deleter)>;
,其中auto deleter = [](struct addrinfo* ptr){if (ptr != nullptr) {freeaddrinfo(ptr);} };
。Clang的swap似乎不需要拷贝分配运算符,但gcc的std::swap需要,正如您在这些日志中看到的那样:
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)
^
标准是什么?我能设法交换这两个std::unique_ptr吗?它们是一种变通方法吗?(也许将lambda封装在std::函数中?…)
编辑:这里有一个小例子,应该或多或少是一样的:
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;
}
错误:
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);
^
这与unique_ptr
或tuple
无关,您可以将错误减少为:
int main()
{
auto deleter = []() { };
auto del2 = deleter;
deleter = static_cast<decltype(deleter)>(del2);
}
它用Clang编译,但用G++失败,给出了这个错误:
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 = []() { };
^
最后一个C++11标准在[expr.prim.lambda]/19:中说
与lambda表达式关联的闭包类型有一个已删除的(8.4.3)默认构造函数和一个已复制赋值运算符。它有一个隐式声明的复制构造函数(12.8),也可能有一个隐含声明的移动构造函数(12.8)
因此,类型是否可移动赋值取决于编译器。
扩展Jonathan Wakely的答案:
当您交换到unique_ptr
时,您还必须交换它们的删除程序您看到的问题可以归结为:clang可以交换两个相同类型的lambda,gcc不能(正如Jonathan引用的那样,标准允许两者)。演示:
#include <utility>
int main() {
auto f = [](){};
auto g(f);
std::swap(f, g);
}
这段代码可以使用clang,但无法使用gcc进行编译。(这没关系。)
这就是它发生的原因
我建议如下:
#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;
}
请注意,代码变得更干净、可读性更强。
如果你绝对必须用lambdas来解决这个问题,那么也许你可以尝试类似的技巧性:只交换指针,而不交换deleter。
#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;
}
虽然这段代码看起来有效,但我真的不喜欢它。我建议第一个不使用lambdas的解决方案。
我可以用以下代码重现类似的错误:
struct A
{
A() = default;
A(A&&) = default;
//A & operator=(A&&) = default;
A(A const & ) = delete;
};
int main()
{
A a, b;
std::swap(a,b);
}
取消对移动赋值运算符的注释,错误就会消失。我猜gcc不允许对lambda进行移动赋值(我使用的是4.7.2版本)。将lambda更改为实际的函数或函子,你应该会没事的。
事实证明,您可以用lambda来解决它,只要它们可以转换为函数指针(lambda什么都不捕获)。
#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;
}
然而,我仍然更喜欢我的另一个具有结构的解决方案。它可能是最有效的一个(由于内联),其次是这里提供的解决方案。如果deleter实现真的很简单,那么传递一个重量级的std::function
对我来说似乎有些过头了。这些性能考虑因素是否重要,由探查器来判断。
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 可组合的lambda/std::函数与std::可选
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 如何建立使用模板函数的lambda函数的尾部返回类型
- C++嵌套if语句,基本货币交换
- 如何将lambda作为模板类的成员函数参数
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- shell排序中的交换和比较
- 在 lambda 捕获中声明的变量的类型推导
- 我可以将调用类的"this"传递给 lambda 函数吗?
- 为什么lambda在clang上崩溃而不是在gcc上崩溃
- 模板函数指针和lambda
- 两组使用lambda函数的大括号
- 使lambda不可复制/不可移动
- FLTK:按下哪个按钮 - 将数字传递给按钮的回调 (lambda)
- 尝试将lambda函数放在队列中时出现一般分配器错误(可能是与unique_ptr有关的错误)
- 将带有unique_ptr的可变 lambda 传递给 const&std::function
- AWS Lambda C++运行时权限被拒绝
- 你能用lambda比较器交换std::队列吗
- 将std::unique_ptr与lambda交换为deleter--GCC