2013-07-05 109 views
8

我們可以使用lambda作爲刪除與std :: unique_ptr嗎?實際上,我是用clang ++來做的,很高興能這樣做。交換std :: unique_ptr lambda as deleter - GCC

我正在使用std::swap轉換爲std::unique_ptr<ObjType, decltyp(deleter)>;,其中auto deleter = [](struct addrinfo* ptr){if (ptr != nullptr) {freeaddrinfo(ptr);} };。鐺的交換似乎並不需要一個拷貝賦值運算符,但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?他們是一種解決方法嗎? (也許封裝拉姆達一個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); 
     ^
+1

你能舉一個你想做什麼的小例子嗎?我試過這個沒有得到一個錯誤:http://ideone.com/LKXz7z –

+1

@VaughnCato:你沒有交換獨特的指針,雖然... –

+0

我已經添加了一個簡短的示例代碼來重現它(我跳它重現相同的東西:)) –

回答

3

要擴大Jonathan Wakely's answer

當你換到unique_ptr S,你也有交換他們的刪除器。你看到的問題歸結爲:鐺可以交換兩個相同類型的lambda,gcc不能(並且該標準允許Jonathan引用它)。示範:

#include <utility> 

int main() { 
    auto f = [](){}; 
    auto g(f); 
    std::swap(f, g); 
} 

此代碼與clang一起使用,但無法使用gcc進行編譯。 (這是行)

這就是爲什麼它發生。


我建議如下:

請注意,代碼變得更清潔,更具有可讀性爲好。


如果絕對有解決這個與lambda表達式,那麼也許你可以嘗試一些的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的解決方案。

+0

正如你所說,「手工」交換指針是最糟糕的解決方案。我發現使用lambda「語義上」更好,因爲它意味着「你正在構建一種函數,而不是使用關鍵字」struct「來構建一個函子(儘管lambda可以(是)作爲一種特殊類型實現的函子)。 –

+0

另一種解決方案是將lambda存儲在std :: function中。然後,std ::函數是可交換的。像: 'std :: function deleter = [](int * p){if(p!= nullptr)free(p); };' –

+0

@ user2535207一般來說,我也更喜歡lambdas結構。然而,它也取決於使用哪一個。如果我不打算重新使用它(不創建可能多個實例),那麼我使用lambda表達式,如果我重用它,則使用結構體。在你的情況下,你正在重新使用它,所以*在我看來*結構在你的情況下更好。但我必須強調,這是我個人的看法。至於表達你的意圖,當我想要一個函子時,我不會太擔心說'struct':我們一直在使用這種方法直到C++ 11,所以它是常識,任何(合理熟練的)開發人員都可以得到它。 – Ali

2

我可以重現一個類似的錯誤用下面的代碼:

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改爲一個實際的函數或仿函數,你應該沒問題。

+0

很傷心。這是GCC錯誤還是'功能'? (即C++ 11說應該允許,或者什麼都不說?) –

+0

不幸的是,我沒有權限訪問這個標準來判斷lambda是否有這樣的限制,但我想不出爲什麼這樣的限制是必要的。這可能只是gcc中的一個bug(或不完整的特性)。 – pelletjl

+1

@pelletjl,沒有人有藉口「我沒有訪問標準」,目前的草案總是可以在左側的http://isocpp.org和[FDIS](http:// www。 open-std.org/jtc1/sc22/wg21/prot/14882fdis/n3290.pdf)與標準 –

5

這有什麼好做unique_ptrtuple,可以將誤差減少到這一點:

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

與鏘編譯但無法與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中說:

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).

所以它是由編譯器決定的,類型是可移動賦值還是不賦值。

1

事實證明,只要它們可以轉換爲函數指針(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; 
} 

不過,我還是喜歡我的other solution與結構更好。這可能是效率最高的一種(感謝內聯),接下來是此處介紹的解決方案。如果刪除器實現非常簡單,那麼傳遞一個重量級的std::function看起來對我來說太過分了。無論這些性能考慮事項是否重要,都是要分析人員的工作。

+0

因爲在我的情況下,不會出現速度問題,我將使用此解決方案,它既簡化了刪除類型,又允許在創建std :: unique_ptrs時寫入刪除程序。謝謝:) –

+0

@ user2535207我很高興我們設法找到您喜歡的解決方案! :) – Ali