potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++ STLでwil::out_param相当の機能を実装する

STLにはWILのout_paramに相当する機能がおそらく存在しませんが、簡単な構造体またはクラスを作成すれば同等の機能を得られます。

STLコンテナにまとめて作成する場合

std::pointer_traitsを利用します。ただし、MSVCでstd::pointer_traitsにポインタ以外を与えた場合はコンパイルエラーが発生します。

C++20から導入されるコンセプト(requires)を使用していますが、コメントアウトすればC++17でも使用可能です。

#include <memory>

template <typename T>
requires std::constructible_from<std::pointer_traits<T>>
struct stl_out_param {
    stl_out_param(T& p) : p1(p), p2(p.get()) {}
    ~stl_out_param() { p1.reset(p2); }
    constexpr operator element_type** () { return &p2; }
private:
    typedef typename std::pointer_traits<T>::element_type element_type;
    T& p1;
    element_type* p2;
};

template <typename T>
requires std::constructible_from<std::pointer_traits<T>>
constexpr stl_out_param<T> out_param(T& p) { return { p }; }

void f(int** p)
{
    *p = nullptr;
}

int main()
{
    auto p1 = std::make_unique<int>(1);
    auto p2 = std::make_shared<int>(2);
    auto p3 = p2;

    f(out_param(p1));
    f(out_param(p2));

    return 0;
}

STLコンテナ別に作成する場合

std::unique_ptrstd::shared_ptrに対してヘルパー関数、構造体を個別に定義するには以下のようにします。

#include <memory>

template <typename T>
struct unique_ptr_out_param {
    unique_ptr_out_param(std::unique_ptr<T>& p)
        : p1(p), p2(p.get()) {}
    ~unique_ptr_out_param() { p1.reset(p2); }
    constexpr operator T** () { return &p2; }
private:
    std::unique_ptr<T>& p1;
    T* p2;
};

template <typename T>
inline constexpr unique_ptr_out_param<T> out_param(std::unique_ptr<T>& p) { return { p }; }

template <typename T>
struct shared_ptr_out_param {
    shared_ptr_out_param(std::shared_ptr<T>& p)
        : p1(p), p2(p.get()) {}
    ~shared_ptr_out_param() { p1.reset(p2); }
    constexpr operator T** () { return &p2; }
private:
    std::shared_ptr<T>& p1;
    T* p2;
};

template <typename T>
constexpr shared_ptr_out_param<T> out_param(std::shared_ptr<T>& p) { return { p }; }

void f(int** p)
{
    *p = nullptr;
}

int main()
{
    auto p1 = std::make_unique<int>(1);
    auto p2 = std::make_shared<int>(2);
    auto p3 = p2;

    f(out_param(p1)); // p1はnullptrを保持する。
    f(out_param(p2)); // p2はnullptrを保持する。
    // p3は変わらない。

    return 0;
}