[英]swap std::unique_ptr with lambda as deleter — GCC
我們可以使用lambda作為帶有std :: unique_ptr的刪除器嗎? Actualy,我用clang ++做了它,很高興這樣做。
我正在使用std::swap
來交換到std::unique_ptr<ObjType, decltyp(deleter)>;
其中auto deleter = [](struct addrinfo* ptr){if (ptr != nullptr) {freeaddrinfo(ptr);} };
。 Clang的交換似乎不需要復制賦值運算符,但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 :: function中?...)
編輯:這是一個應該或多或少相同的小例子:
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)。
因此,編譯器是否可以移動 - 可分配。
當您切換到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 來解決這個問題,那么也許你可以嘗試像這樣的hackish :只交換指針而不是刪除者。
#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不允許移動分配lambas(我使用的是版本4.7.2)。 將lambda更改為實際函數或函子,你應該沒問題。
事實證明,你可以用lambdas解決它,只要它們可以轉換為函數指針(lambdas捕獲任何東西)。
#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;
}
但是,我仍然喜歡我的結構更好的其他解決方案 。 它可能是最有效的(由於內聯),其次是此處提供的解決方案。 如果刪除器實現非常簡單,那么傳遞一個重量級的std::function
對我來說就像是一種矯枉過正。 無論這些性能因素是否重要,它都是分析器的工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.