2013-07-05 84 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