potisanのプログラミングメモ

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

C++ STLスマートポインタのconst修飾の不思議と解釈

STLのスマートポインタ(std::unique_ptr)のgetメンバー関数がT* get() constと戻り値にconstが存在しないことに悩んでいましたが、以下の質問サイトの回答を読んで腑に落ちました。スマートポインタの指す値をconstにするにはTconst Tとするようです。

stackoverflow.com

上記の回答と実際のコードをあわせて考えると以下のようです。「○」はコンパイル可能、「×」はコンパイルエラーを表しています。

生ポインタ スマートポインタ t = ... *t = ...*t.get() = ...
T* t std::unique_ptr<T> t
const T* t std::unique_ptr<const T> t ×
T* const t const std::unique_ptr<T> t ×
const T* const t const std::unique_ptr<const T> t × ×
  • std::unique_ptr<const T> pconst T* pのように振る舞う。
  • const std::unique_ptr<const T> pconst T* const pのように振る舞う。
  • const std::unique_ptr<T> pT* const pのように振る舞う。

STLスマートポインタのconst修飾確認用コード

#include <memory>
#include <string>

int main()
{
    auto p1 = std::make_unique<const int>(123);
    p1 = nullptr;
    *p1.get() = 456; // コンパイルエラー。p1の指す値はconst。

    const auto p2 = std::make_unique<int>(123);
    p2 = nullptr;    // コンパイルエラー。p2自体の値はconst。
    *p2.get() = 456;

    const auto p3 = std::make_unique<const int>(123);
    p3 = nullptr;    // コンパイルエラー。p3自体の値はconst。
    *p3.get() = 456; // コンパイルエラー。p3の指す値はconst。

    return 0;
}

生ポインタのconst修飾確認用コード

int main()
{
    int x = 123;

    const int* p1 = &x;       // int const* p1...でも同様。
    p1 = nullptr;
    *p1 = 456;                // コンパイルエラー。p1の指す値はconst。

    int* const p2 = &x;
    p2 = nullptr;             // コンパイルエラー。p2自体の値はconst。
    *p2 = 456;

    const int* const p3 = &x; // int const* const p1...でも同様。
    p3 = nullptr;             // コンパイルエラー。p3自体の値はconst。
    *p3 = 456;                // コンパイルエラー。p3の指す値はconst。

    return 0;
}

補足

  • メンバー関数の引数リストの後にあるconstT member_fn(...) const)はフィールドの値自体の不変だけを要求します。getの戻り値を間接参照*で変更してもスマートポインタの管理するフィールドは変化しないので、この仕様は満たされています。