【C++】constメンバ関数の詳細解説と各種エラーの対処方法

constオブジェクトのメンバ関数が呼び出せずエラーが発生してしまった。LLDBのデバッガコマンドも使えない。

struct Number {
  int i = 0;
  void print() { printf("%d", i); }
};

const Number value = Number();
value.print();
// (lldb) po value.print()
// error: member function 'print' not viable: 'this' argument has type 'const Number' (aka 'const Number'), but function is not marked const
// note: 'print' declared here
// error: 1 errors parsing expression

この場合、print関数にconst修飾子を指定することで対処出来る。

struct Number {
  int i;
  void print() const { printf("%d", i); } // これ
}

const Number value();
value.print(); // "hello"
// (lldb) po value.print()
// "hello"

constメンバ関数

ここで使用したconst修飾子には、対象の関数がメンバ変数を変更しないものであることを明示する役割がある。こうして宣言された関数はconstメンバ関数と呼ばれ、const宣言されたオブジェクトからも呼び出せるようになる。

当然、const修飾された関数はメンバ変数の変更が出来なくなる。

struct Number {
  int num;
  // ERROR: Cannot assign to non-static data member within const member function 'print'
  void print() const { num = 99; }
};

非constメンバ関数の呼び出し

またconstメンバ関数から非constメンバ関数を呼び出すことも当然出来ない。

struct Number {
  int num;
  // ERROR: Member function 'setValue' not viable: 'this' argument has type 'const Number', but function is not marked const
  void print() const { setValue(123); }
  void setValue(int value) { num = value; }
};

mutable修飾子

ただし、mutable宣言されたメンバ変数はconstメンバ関数からでも例外的に変更できるようになる。

struct Number {
  int mutable num;
  void print() const { num = 456; } // OK
};
constオブジェクト本来の用途を考えれば、むやみやたらに使うべき機能ではないが、演算用のキャッシュやオブジェクトの参照カウントを保持する等、例外的な用途での利用シーンが考えられる。

メンバ変数の関数呼び出し

constメンバ関数はメンバ変数の非メンバ変数を呼び出せないという特徴もある。ただし、ポインタ変数のメンバ関数については問題なく呼び出せてしまう点に注意したい。

struct Number {
  int num;
  void print() const {}
  void setValue(int value) { num = value; }
};

struct Test {
  Number  a;
  Number* b;
  const Number* c;
  
  void test() const {
    // OK: constメンバ関数なので呼び出せる
    a.print();
    // NG: constメンバ関数でないので呼び出せない
    a.setValue(8);
    // OK: ただし、ポインタの場合は呼び出せてしまう
    b->setValue(8);
    // constメンバ関数の制限はポインタ変数の書き換えに対して行われるためである
    // ERROR: Cannot assign to non-static data member within const member function 'test'
    b = nullptr;
    // NG: const変数へのポインタであれば呼び出せなくなる
    c->setValue(8);
  }
};

非constメンバ関数が呼び出せない理由

constメンバ関数からメンバ変数の書き換えや非constメンバ関数の呼び出しが行えない理由は、constメンバ関数内のthisポインタが暗黙的にconst修飾されるためである。

以下のコードと同様の解釈が行われるため、自身の書き換えや非constメンバ関数の呼び出しが行えなくなっている。

void print() const {
  const Number* that = this;
  that->num = 1;     // const変数なので書き換え不可
  that->setValue(2); // 非constメンバ関数なので呼び出し不可
}

広告