[C++] constと非constのアクセサ関数を簡単に実装する

アクセサ(setterやgetter)を実装する際に,Publicなconst,非const関数の両者実装したい場合がある
直観的にはconst,非const関数をそれぞれ別名で以下のように実装するだろう.

public:
  const Hoge* ConstGetter() const { /* 実際に返す処理(getter)を記述する */ }
  Hoge* NonConstGetter() { /* 実際に返す処理(getter)を記述する */ }

関数名をconstとnon-constで使い分けるのは,面倒です...

正直なところ関数名が別々であるとわかりにくい.単純に上のようにConst…とNonConst…としてもいいが,関数名が長くなる.場合によっては,似たような関数が増えていくと命名に悩むこともあるだろう.
利用者はアクセサのconst,非constをあまり意識せずに使いたいというのが本音である.(クラスそのもののconstは意識したほうが良いが...)
この問題を解決するには,C++の仕様「const,非constメンバ関数はオーバライドが可能である」をいうことを利用すると比較的簡便に実装できる.

class Foo
{
public:
    Foo(){ }
    const int * GetValue() const { return this->value_; }
    int * GetValue() { return this->value_; }

private:
    int* value_ = nullptr;

};
int main()
{
    { // const
        const Foo foo;
        auto ptr = foo.GetValue(); // const int * GetValue() const が呼ばれる
    }
    { // non-const
        Foo foo;
        auto foo.GetValue(); // int * GetValue() が呼ばれる
    }
    return 0;
}

このように実装することで,利用者はクラスそのもののconst,非constを意識するだけで,自動的にアクセサconstの有無を識別してくれる.

上記に示した例は極めて簡単な例題であり,実用上はメンバ変数に直接返す関数はあまり使用されない.Getter自体が何らかの処理をし,値もしくはクラスを返すという処理をするだろう.Getterの中身の複雑になるもしくは記述量が増えると,同じ実装を記述するのはDRY原則に反する

目次

効率的なconst,非constのアクセサ関数の実装

const,非constのアクセサ関数のベストプラクティスとして,次のような実装である(と思う).

public:
  Hoge* GetHoge() { return GetHogePrivate(); }
  const Hoge* GetHoge() const { return GetHogePrivate(); }
private:
  Hoge GetHogePrivate() const { /* 実際に返す処理(getter)を記述する */ }

PrivateなGetter (“Hoge GetHogePrivate() const”)を用意し,PublicなGetterがconst (“const Hoge* GetHoge() const”),非const (“Hoge* GetHoge()”)に変換しreturnするという動作になる.Getter自体を変更する際には,PrivateなGetterのみを変更することで対応できる.
もし,PrivateなGetterを用意せず, const,非constのGetterに対し同じ実装をしていた場合,実装時にコピペミスなどの可能性があり,危険である.

Const,non-constのGetterはPublic宣言し,

実装はPrivateなGetterを使えばOK!

メリット

  1. const,非const関数に対し,それぞれ実装を記述する必要がない.(DRY原則)
  2. 実装が比較的わかりやすく,記述量も少ない

その他の実装方法

上述した実装方法以外のDRY原則に則ったconst,非constメンバ関数の実装方法を紹介する.

const_castを使用する方法

const_castを利用する最も単純な発想に基づいたもの.以下のような実装になるだろう.関数名を別名にしているのは,同名の場合うまくいかないためである(関数が再帰的に呼び出される).

public:
  const Hoge* GetHogeConst() const { /* 実際に返す処理(getter)を記述する */ }
  Hoge* GetHogeNonConst() { return const_cast<Hoge*>(GetHogeConst()); }

デメリット

  1. 同名のアクセサが使用できないため,関数を別名にしないといけない.
  2. const_castを実装すること自体が面倒->タイポする可能性あり.
  3. const_castの処理時間がかかるのでは?(実測していないので不明)

1.の「別名の関数を用意しないといけない問題」に関しては,下記サイトで代替方法を記載している.発想は素晴らしいがやはり実装自体が面倒な気がする.

Flat Leon Works
【C++ アイデア】const_castをちょっと楽にする - Flat Leon Works const_castはちょっと手間 const_on / const_off 関数を作る 使い方 まとめ const_castはちょっと手間 const_castはconstを付けたり外したりするためのキャストなのですが...

結論

Const,非Constのアクセサ(Getter)は次のようなコードを使用しよう.

public:
  Hoge* GetHoge() { return GetHogePrivate(); }
  const Hoge* GetHoge() const { return GetHogePrivate(); }
private:
  Hoge GetHogePrivate() const { /* 実際に返す処理(getter)を記述する */ }

https://amzn.to/3yqWDHq

https://amzn.to/2Vc0VEm

あわせて読みたい
Amazon.co.jp: 達人プログラマー(第2版): 熟達に向けたあなたの旅 : Andrew Hunt, David Thomas, 村上雅章:... Amazon.co.jp: 達人プログラマー(第2版): 熟達に向けたあなたの旅 : Andrew Hunt, David Thomas, 村上雅章: 本
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次