ゲーム作りは楽しい

なんか書く

constexprでテンプレートメソッドパターンやろうとしたら、よくわからんくなった話

そもそもテンプレートメソッドパターンとは、以下のようにアクセスするpublicのメンバ関数内で、(純粋)仮想関数などを使用し、決められた型にはまった動作を行うようにするデザインパターンです。

class Original
{
protected:
	int m_num;

	virtual int cal()const=0;

public:
	Original(const int num):
		m_num(num)
	{}

	void value()const
	{
		cout << "値は:"<< this->cal()<<endl;
		
	}
};

class Square : public  Original
{
private:
	int cal() const override
	{
		return m_num*m_num;
	}

public:

	Square(const int num):
		Original(num)
	{}

};

//後で比較するためにあえてここに作る
const Square num(2);

void main()
{
	num.value();
}

これの場合は

  1. 「値は」と出力
  2. 何かわからんがcal関数で計算された値が出力

というだけの枠組みに決まったvalue()関数である。


ですがconstexprを学びだした僕はこう思ったわけです
ん?おいまてよ、これくらいならconstexprにしたいぞと、

ってことでconstexprにしてみる

class Original
{
protected:
	int m_num;

	virtual int hoge()const=0;

public:
	constexpr Original(const int num):
		m_num(num)
	{}

	void value()const
	{
		cout << "値は:"<< this->hoge()<<endl;
		
	}
};

class Square : public  Original
{
private:
	int hoge() const override
	{
		return m_num*m_num;
	}

public:

	constexpr Square(const int num):
		Original(num)
	{}

};

constexpr Square num(2);

void main()
{

	num.value();
}

やってやったぞ!優勝!!!と思ったあなたは大間違い

これを実行すると…動作が停止します。

thisがねえって怒られます。
constexprはvirtualメソッドがNGって聞きます。つまりはダメ

ちなみに下みたいにしたら問題なく動いたのですが、その理由がわからない(T T)

void main()
{
	constexpr Square num(2);

	num.value();
}


h内に書いたりした場合ももちろん動作は停止。

では、どうやって解決するかですが

templateを使って静的ポリモーフィズムのようにします

template<class _T>
class Original
{
protected:
	int m_num;

public:
	constexpr Original(const int num):
		m_num(num)
	{}

	void value()const
	{
		cout << "値は:"<< static_cast<const _T&>(*this).cal()<<endl;
		
	}
};

class Square : public  Original<Square>
{
	friend class Original<Square>;
private:
	int cal() const
	{
		return m_num*m_num;
	}

public:

	constexpr Square(const int num):
		Original(num)
	{}

};

おいおいfriendがでてきたぞと、
friendとはそのclassに自身のprivateやprotectedのメンバへのアクセスを許可するものです。
hogeをpublicにすればいいのですが、それではテンプレートメソッドを使ってるメリットがなくなってしまいます。

これでOKですが…

Originalにhogeさえ持ってればどんなclassでも_Tに渡せる状態なのでわりと危険かな
(static_castしてるのでそこでエラーは、はいてくれるが)


templateを使うたびにやばいこと(危険なこと)してる気がしてます。

関数のオーバーロードは静的に決まるの話

関数のオーバーロードは静的に決まることを知らなくてはまったことがあったのでメモ

class Super 
{
};

class Sub :public Super
{
};

void Piyo(const Super& super)
{
	cout << "Super" << endl;
}

//関数のオーバーロード
void Piyo(const Sub& sub)
{
	cout << "Sub" << endl;
}

void main()
{

	unique_ptr<Super> hoge = make_unique<Sub>();
	Piyo(*hoge);

}

これを実行すると何が表示されるでしょうか?
実行結果は

Super

です。

タイトル通り、関数のオーバーロードは静的に決まるので、hogeはunique_ptr< Super >からSuperとしか解決できません。

もしこういったことがしたい場合はvisiterパターンのような方法がひつようになります。

class Super 
{
public :
	virtual void piyopiyo()
	{
		Piyo(*this); //おい、俺はSuperだぞ
	}
};

class Sub :public Super
{
public:
    //関数のオーバーライドも忘れずに
	void piyopiyo()override
	{
		Piyo(*this); //おい、俺はSubやで^^
	}

};

void Piyo(const Super& super)
{
	cout << "Super" << endl;
}

//関数のオーバーロード
void Piyo(const Sub& sub)
{
	cout << "Sub" << endl;
}

void main()
{

	unique_ptr<Super> hoge = make_unique<Sub>();

	hoge->piyopiyo();

}

これで実行結果は

Sub

となります。

書いていることは全く同じですが、派生classでオーバーライドを忘れないようにしましょう。