他のアカウント

無料ブログはココログ

« 日本国憲法、自民党改憲案、中国憲法、北朝鮮憲法等の前文を比べてみた | トップページ | 自由権、参政権、社会権−素人の備忘録 »

2017年6月25日 (日)

C++ で自分自身の型を表す方法や、thisポインタの偽物作りとかやってみた

C++言語には、「自分自身の型」を表す標準的な記法がない。C++11 からは decltype(*this) で可能になったが、自由に使えるわけではない。例えば、メンバ関数の外側で

using ThisClass = decltype(*this) ;

などのような使い方はうまくできない。

また、Fooというクラスの中では decltype(*this) は Foo& 型なので、Foo型を使いたければ参照をはずさなければならない。

そこで、少々トリッキーでもよいからそのような方法を考えてみた。とは言っても、自分の力では無理なので、あちこち検索した結果の切り貼り組み合わせが主である。

プログラムの流れは 自分の型を取得 を参照した。

C++:自クラスの型にアクセスできる方法はないでしょうか?に、メンバ関数外で自分自身の型を表す名前を取得する方法として、static関数宣言を利用するトリックがあったので、これで一応できることが分かった。が、意味的におかしいので正統的な方法ではない。コンパイルエラーとすべきもの。
また、これが継承で伝播されるか?と一応試してみたが、予想通り伝播しなかった。

さらに、「thisポインタの偽物」を作ると const_cast や mutable を使わなくともconst性を弄れるので、それも追加した。これは自作。

これは、

class Foo{
....
private:
Foo* const m_mythis = this;
public:
Foo* const mythis(){ return m_mythis;}
....
};

ということをやると、const Foo x; としても、*(x.mythis()) はxを表すがconst性は持たない、という代物である。(g++では動いたが、コンパイラによって挙動が変わるかもしれない。)

これは簡単にできるので面白くなかったのだが、むしろこんな簡単にトリッキーなコードが書けるのか!と、C++の恐ろしさを感じた。
const_castやmutable が書いてあればまだ「const性を弄っているな」と分かるが、それなしでconst性が外せるのは、うっかりミスが怖い。

この偽物のthisを使うと「const な代入演算子」などが定義できる。

以下のコードは Ubuntu 上 g++ ver.4.8.4 (-std=c++11 オプション付き)で通った。

ただし、clang++ では「staticメンバ関数の宣言に this は使えませんよ!」と叱られてエラーとなる。

つまり、g++の型推論のチェックの甘さを利用したコードになるので、当然、このコードを実行した場合の結果にはまったく責任が取れないことになる。

ソース:

//thisclass-main.cpp
#include <iostream>

class Foo{
public:
virtual ~Foo() = default;
Foo(){}
Foo( const Foo& f ){ std::cout<< "コピーコンストラクタ" << std::endl;}
Foo( Foo&& f ){ std::cout<< "ムーブコンストラクタ" << std::endl;}
Foo& operator=( const Foo& f ){
std::cout << "代入" << std::endl;
return *this;
}
Foo& operator=( Foo&& f ){
std::cout << "ムーブ代入" << std::endl;
return *this;
}

static auto dummythis()->decltype(this); //宣言のみ
// Foo* 型を表す型
using This = decltype(dummythis());

static auto dummythisref()->decltype(*this);//宣言のみ
// Foo& 型を表す型
using ThisRef = decltype(dummythisref());
// Foo 型を表す型
using ThisClass = std::remove_reference<ThisRef>::type;

ThisClass myself(){ return *this;}
// トップレベルで ThisClass 使用

private:
This const m_mythis = this;
// thisポインタの「偽物」
public:
This const mythis() const {return m_mythis;}

// m_mythis はポインタなので、 const Foo x; としても
// *(x.mythis()) に const性はない。
// const 性はポインタによって伝播されないため。
// const_cast や mutable を使わずにconst性を除去可能。
// (コンパイラによって挙動が異るかもしれない)。
// これ自身は自分の型を表す名前作成とは独立の話。

// const な代入演算子が可能。通常の代入演算子とオーバーロードされる。
Foo& operator=( const Foo& f ) const {
std::cout << "const ";
*m_mythis = *(f.mythis());
return *m_mythis;
}

virtual void foo(){
ThisClass::bar(); //この書き方が可能
}

static void bar(){
std::cout << "bar" << std::endl;
}
};


class Deriv : public Foo{
public:
virtual void foo() override {
// ThisClass::bar2();
// 「Foo::ThisClass に bar2メンバ関数がない」エラー
// 継承したからといってThisClass がDeriv型になるわけではない。
}

static void bar2(){
std::cout << "bar2" << std::endl;
}
};


int main(){
Foo().foo();
Foo().myself().foo();
const Foo x;
Foo y;
x = y; // const 代入!
Deriv().bar2();
return 0;
}

実行結果:

bar
コピーコンストラクタ
bar
const 代入
bar2

以上。

« 日本国憲法、自民党改憲案、中国憲法、北朝鮮憲法等の前文を比べてみた | トップページ | 自由権、参政権、社会権−素人の備忘録 »

C/C++言語」カテゴリの記事

パソコン・インターネット」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/525300/65456933

この記事へのトラックバック一覧です: C++ で自分自身の型を表す方法や、thisポインタの偽物作りとかやってみた:

« 日本国憲法、自民党改憲案、中国憲法、北朝鮮憲法等の前文を比べてみた | トップページ | 自由権、参政権、社会権−素人の備忘録 »