プライベートは大切にしたいほうです。
このアーティクルを書いている今、個人的な話ですが仕事がデスマーチでプライベートなんてありません。
普段なら、この時期はプライベートジェットでサイパンに飛んで、プライベートビーチに寝転がってiPadでプライベートライアンを見ているはずです。
自分のプライベートが守られない今、他人のクラスのプライベートも守りません。積極的に攻めていくスタイルです。
1 2 3 4 5 6 7 8 |
// 従業員 class Employer { float time_for_game_; // ゲームの時間 float time_for_anime_; // アニメの時間 float time_for_family_; // 家族との時間 public: float time_for_work_; // 仕事の時間 }; |
雇われサラリーマンにも生活があります。
仕事の時間の他に自分の時間も必要なのです。そして、その部分というのは完全にプライベートなのです。
この従業員クラスをもうすこし価値のあるものにしてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 従業員 改訂版 class Employer { float time_for_game_; // ゲームの時間 float time_for_anime_; // アニメの時間 float time_for_family_; // 家族との時間 public: float time_for_work_; // 仕事の時間 // 挨拶する ハロー☆ template <class Rank> void greet(Rank target) { std::cout << "hello" << std::endl; } }; |
挨拶できるようになりました。
しかも挨拶する対象の職位に応じて挨拶を変えられます。
例えば
1 2 3 4 5 |
// 社長にだけは挨拶じゃなくて休暇をせがむ template<> void Employer::greet(President target) { std::cout << "holiday please" << std::endl; } |
社長の時だけは休暇をせがむようテンプレートを特殊化しました。
1 2 3 4 5 6 7 8 9 |
int main() { President takeda; Employer mami; Employer jun; jun.greet(mami); jun.greet(takeda); return 0; } |
1 2 3 |
出力: hello holiday please. |
では外部から、このEmployerのprivateメンバにアクセスしてゲームの時間をごっそり仕事の時間に移すことは可能でしょうか。
アクセス指定子の無効化というアーティクルの中で、privateメンバにもアクセスする術について記載しました。あれはC++の言語ルール的にはまったくの違法行為です。
privateというc++のキーワードを再定義してはいけません。
では合法的にやってみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// Employerが定義されているファイルをインクルードする #include "employer.hpp" // ↓↓ ここからがキモ namespace { class X{}; } template <> void Employer::greet(X target) { time_for_work_ += time_for_game_; // ゲームの時間を仕事の時間に足す time_for_game_ = 0; // ゲームの時間なんてゼロだ! } // ↑↑ ここまでがキモ int main() { // ゲームの為の時間を表示 Employer jun; jun.printTime(); jun.greet(X()); // 勝手に特殊化したgreetを呼ぶ jun.printTime(); return 0; } |
1 2 3 |
出力 game 5 work 5 game 0 work 10 |
メンバにテンプレート関数があると、外部でその関数の特殊化を行うことができます。これを利用して本来不可侵だったはずのprivateメンバの内容を意のままに書き換えました。
しかも、アクセス指定子の無効化と違い、今回のアプローチは合法です。特殊化したメンバ関数は、まったく議論の余地がない正式なクラスのメンバ関数ですのでc++の言語ルールに一切抵触していません。
この男は言語ルールの抜け道を知っている法律家だ。彼が捕まることは絶対にない。というのも、彼は法の精神を侵蝕する間も、注意深く条文に従っているからだ
__Exceptional C++ Style アクセス制御の使用と誤用
このアプローチが法に触れそうになる瞬間、それは特殊化に使用した型が既に別の箇所で同様の特殊化に使われており、結果この関数定義がODRに違反する場合です。しかしそれも無名ネームスペースに定義したオリジナルの型を用いることにより簡単に回避することができます。
まとめ
- あるクラスがテンプレートメンバ関数を持っている場合、内部情報に合法的にアクセスできる
- ODR違反を回避する為に特殊化に使用する型は無名ネームスペース内で定義する
- プライベートは大事