8

std::unique_ptr でラムダをデリータとして使用できますか? 実際、私はclang ++でそれを行いましたが、喜んでそうしました.

どこstd::swapにスワップするために使用しています。Clang の swap はコピー代入演算子を必要としないようですが、gcc の std::swap はそれらのログでわかるように必要でした:std::unique_ptr<ObjType, decltyp(deleter)>;auto deleter = [](struct addrinfo* ptr){if (ptr != nullptr) {freeaddrinfo(ptr);} };

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

基準は何と言いますか?これら 2 つの std::unique_ptr をどうにかして wap できますか? それらは回避策ですか?(おそらくラムダを 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);
           ^
4

4 に答える 4

5

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 で次のように述べています。

ラムダ式に関連付けられたクロージャー型には、削除された (8.4.3) デフォルト コンストラクターと削除されたコピー代入演算子があります。暗黙的に宣言されたコピー コンストラクター (12.8) を持ち、暗黙的に宣言された移動コンストラクター (12.8) を持つ場合があります。

したがって、型が移動代入可能かどうかはコンパイラ次第です。

于 2013-07-05T23:14:51.080 に答える
3

ジョナサン・ウェイクリーの答えを拡張するには:

s にスワップするときはunique_ptr、それらのデリータもスワップする必要があります。あなたが見ている問題はこれに要約されます.clangは同じタイプの2つのラムダを交換できますが、gccはできません(そして、ジョナサンが引用しているように、標準では両方が許可されています)。デモンストレーション:

#include <utility>

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

このコードは clang では機能しますが、gcc ではコンパイルできません。(そしてそれはOKです。)

それが起こっている理由です。


次のことをお勧めします。

#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;
}

コードがよりきれいになり、読みやすくなったことに注意してください。


これをラムダで絶対に解決する必要がある場合は、おそらく次のようなハックなことを試すことができます:ポインターのみを交換し、デリータは交換しません。

#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;
}

このコードは機能しているように見えますが、私は本当に好きではありません。ラムダを使用しない最初のソリューションをお勧めします。

于 2013-07-05T21:47:50.553 に答える
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 がランバの移動割り当てを許可していないと推測しています (私はバージョン 4.7.2 を使用しています)。ラムダを実際の関数またはファンクターに変更すれば問題ありません。

于 2013-07-05T17:36:46.053 に答える
1

結局のところ、関数ポインターに変換できる限り、ラムダで解決できます (ラムダは何もキャプチャしません)。

#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デリーターの実装が本当に単純な場合、重いものを渡すのはやり過ぎのように思えます。これらのパフォーマンスに関する考慮事項が重要かどうかを判断するのは、プロファイラーの仕事です。

于 2013-07-06T13:27:09.130 に答える