移动包含 lambda 的元组

Move tuple containing lambda

我正在尝试实施通用 RAII 清理。这看起来像是在工作,直到我尝试使用移动构造函数:

#include <functional>
#include <tuple>
#include <utility>

template <typename R, typename D>
class Cleaner {
public:
  Cleaner() = default;
  Cleaner(R r, D d = {}) : data(std::move(d), std::move(r)), owns(true) {
  }
  ~Cleaner() {
    if (!owns) return;
    std::apply(
      [](auto&&... args){
        std::invoke(std::forward<decltype(args)>(args)...);
      },
      std::move(data)
    );
  }
  Cleaner(Cleaner&& other) noexcept {
    swap(*this, other);
  }
  Cleaner& operator=(Cleaner&& other) noexcept {
    swap(*this, other);
    return *this;
  }

  void release() { owns = false; }

  R& get() { return std::get<1>(data); }
  R const& get() const { return std::get<1>(data); }

  friend void swap(Cleaner& a, Cleaner& b) noexcept {
    using std::swap;
    swap(a.data, b.data);
    swap(a.owns, b.owns);
  }

private:
  std::tuple<D, R> data;
  bool owns = false;
};

#include <iostream>

int main() {
    auto c = Cleaner(0, [](int){ std::cout << "clean\n"; });
    auto a(std::move(c)); // <- this fails to compile
}

(编译报错信息太长复制到这里,不过你可以在这里看到:https://godbolt.org/z/5fqz5qTxT

消息开头为:

source>: In instantiation of 'Cleaner<R, D>::Cleaner(Cleaner<R, D>&&) [with R = int; D = main()::<lambda(int)>]':
<source>:48:24:   required from here
<source>:20:37: error: no matching function for call to 'std::tuple<main()::<lambda(int)>, int>::tuple()'
   20 |   Cleaner(Cleaner&& other) noexcept {
      |                                     ^
In file included from /opt/compiler-explorer/gcc-trunk-20220208/include/c++/12.0.1/functional:54,
                 from <source>:1:
/opt/compiler-explorer/gcc-trunk-20220208/include/c++/12.0.1/tuple:1259:9: note: candidate: 'template<class _Alloc, class _U1, class _U2, typename std::enable_if<__is_explicitly_constructible<_U1, _U2>(), bool>::type <anonymous> > std::tuple<_T1, _T2>::tuple(std::allocator_arg_t, const _Alloc&, std::pair<_U1, _U2>&&) [with _U1 = _Alloc; _U2 = _U1; typename std::enable_if<std::_TupleConstraints<true, _T1, _T2>::__is_explicitly_constructible<_U1, _U2>(), bool>::type <anonymous> = _U2; _T1 = main()::<lambda(int)>; _T2 = int]'
 1259 |         tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>&& __in)
      |         ^~~~~  
[...]

有人可以帮助我了解问题所在以便我解决吗?

你的问题是 lambda 不是默认可构造的(non-capturing 是自 C++20 起)。

您可以将构造函数更改为:

Cleaner(Cleaner&& other) noexcept :
    data(std::move(other.data)),
    owns(other.owns)
{
    other.owns = false;
}

Demo