ラムダ式でムーブキャプチャ

ラムダ式での変数のキャプチャはコピーまたは参照で行いますが,ムーブするための標準的な方法はありません.

std::unique_ptr<int> p(new int(23));

auto f = [std::move(p)] { std::cout << *p << '\n'; }; // こんな書き方はない

どうしても変数 p をムーブしたい場合には,別の変数 p_ を用意して,p_ がコピーキャプチャされたときに p がムーブされるようにする方法があります.以下にそのためのクラステンプレート move_capture を示します.

#include <cassert>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>

template <class T>
struct move_capture
{
private:
    bool has_obj;
    union { T *ptr; T obj; };

public:
    move_capture(T &t) noexcept
      : has_obj(false), ptr(std::addressof(t))
    {}

    move_capture(move_capture const &x)
    {
        construct(x);
    }

    move_capture(move_capture &&x)
      : has_obj(x.has_obj)
    {
        if (x.has_obj)
            ::new(std::addressof(obj)) T(std::move(x.obj));
        else
            ptr = x.ptr;
    }

    move_capture &operator=(move_capture const &) = delete;

    ~move_capture()
    {
        if (has_obj)
            obj.~T();
    }

    T &get() noexcept
    {
        assert(has_obj);
        return obj;
    }

    T const &get() const noexcept
    {
        assert(has_obj);
        return obj;
    }

private:
    template <class U, typename std::enable_if<std::is_copy_constructible<U>::value>::type * = nullptr>
    void construct(move_capture<U> const &x)
    {
        has_obj = true;
        if (x.has_obj)
            ::new(std::addressof(obj)) T(x.obj);
        else
            ::new(std::addressof(obj)) T(std::move(*x.ptr));
    }

    template <class U, typename std::enable_if<!std::is_copy_constructible<U>::value>::type * = nullptr>
    void construct(move_capture<U> const &x)
    {
        assert(!x.has_obj);
        has_obj = true;
        ::new(std::addressof(obj)) T(std::move(*x.ptr));
    }
};

template <class T>
inline move_capture<T> make_move_capture(T &t)
{
    return move_capture<T>(t);
}

使い方としては,make_move_capture で p から p_ を作り,それをコピーキャプチャします.

// ポインタの参照先を表示する関数テンプレート
template <class P>
void print(P const &p)
{
    if (p)
        std::cout << *p << '\n';
    else
        std::cout << "null\n";
}

void example()
{
    std::unique_ptr<int> p(new int(23));

    auto p_ = make_move_capture(p);
    print(p); // 23

    auto f = [p_]
    {
        // 元の変数の値にアクセスするときは .get() を使う
        print(p_.get());
    };
    print(p); // null
    f(); // 23

    auto g = std::move(f);
    f(); // null
    g(); // 23
}

もっとも,どうしてもムーブキャプチャしなければならない場面はそんなにないと思います.