😺

【C++】const修飾されたクラスのメンバポインタ【Bloggerからの移行記事】

に公開

誤った方法

const修飾されたクラスのメンバポインタ型は普通コンパイルエラーで作ることができません。

class test {};

using const_class_value_pointer = int test const::*;
using const_class_function_pointer = int (test const::*)(int);

型名はクラスを指していませんというエラーがでます。(const testはクラスではないらしい)
しかし、以下のようにすればコンパイルを通すことができます。(テンプレートを用いても通すことができる)

using c_test = const test;

using const_class_value_pointer = int c_test::*;
using const_class_function_pointer = int (c_test::*)(int);

結局のところ、参照の圧縮と同じような感じでconstが勝手に外されるだけです。


static_assert(is_same_v<int test::*, int c_test::*>);
static_assert(is_same_v<int (test::*)(int), int (c_test::*)(int)>);

同様にvolatileも外されます。

結局どうすればいいのか

メンバ変数

メンバ変数はオブジェクトのconst修飾の有無に関わらず、int test::*のようなメンバポインタで取得することができます。
その際の戻り値のconst修飾の有無は、アクセスするオブジェクトのconst修飾の有無に準じます。

メンバ関数

メンバ関数は、その宣言時のconst修飾の有無で、呼び出すオブジェクトのconst修飾を制限することができます。
そのため、クラス名の部分ではなく関数型の部分でconst修飾をつけることでconst修飾されたクラスのメンバ関数ポインタを受けることができます。


class foo {
public:
    int foo_function(int){
        return 1;
    }

    int foo_function(int) const{
        return 2;
    }
};

int (foo::*foo_function_pointer)(int) = &foo::foo_function;
int (foo::*const_foo_function_pointer)(int) const = &foo::foo_function;

foo a;
a.*foo_function_pointer(-1); // 1
a.*const_foo_function_pointer(-1); // 2

foo const b;
//b.*foo_function_pointer(-1); // コメントアウトを外すとコンパイルエラーになる
b.*const_foo_function_pointer(-1); // 2

ちなみにint (foo::*)(int) const型のconstは関数型の方についていて、そのことは以下のコードでわかります。


template<typename T>
class get_member_pointer_ret_type;

template<typename Ret, typename Class>
class get_member_pointer_ret_type<Ret Class::*>
{
public:
    using type = Ret;
}


static_assert(is_same_v<get_member_pointer_ret_type<int (foo::*)(int) const>::type, int (int) const>);

GitHubで編集を提案

Discussion