CRTPによるミックスイン
コンパイル時に振る舞いを組み合わせます
「CRTPによるミックスイン」はCoddyKit上の無料C++ Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC++ Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C++ Academyコースには全4レッスンが含まれています。
ミックスインとは
ミックスインは、クラスに組み込んで使用する、小さく再利用可能な動作の単位です。CRTPを使用すると、ホストクラスを呼び出しながら、コンパイル時に機能を追加できます。
シンプルなミックスイン
このミックスインは、ホスト側にvalue()が提供されていることを前提とするprint_twiceメソッドを追加します。
#include <iostream>
template <typename T>
struct PrintTwice {
void print_twice() const {
auto v = static_cast<const T*>(this)->value();
std::cout << v << " " << v << "\n";
}
};
struct Box : PrintTwice<Box> {
int value() const { return 7; }
};
int main() {
Box{}.print_twice();
return 0;
}複数のミックスインを組み合わせる
複数のCRTPミックスインを同時に継承し、独立した動作を1つのクラスに積み重ねることができます。
#include <iostream>
template <typename T>
struct Loggable {
void log() const { std::cout << "log: " << static_cast<const T*>(this)->id() << "\n"; }
};
template <typename T>
struct Resettable {
void reset() { static_cast<T*>(this)->set(0); }
};
struct Device : Loggable<Device>, Resettable<Device> {
int v = 5;
int id() const { return v; }
void set(int x) { v = x; }
};
int main() {
Device d;
d.log();
d.reset();
d.log();
return 0;
}ミックスインは状態も追加する
ミックスインは独自のデータメンバーを持つこともでき、それらは組み合わされたオブジェクトのメモリレイアウトの一部になります。
#include <iostream>
template <typename T>
struct Tagged {
int tag = 0;
void set_tag(int t) { tag = t; }
int get_tag() const { return tag; }
};
struct Item : Tagged<Item> {
const char* name = "item";
};
int main() {
Item it;
it.set_tag(42);
std::cout << it.name << " " << it.get_tag() << "\n";
return 0;
}ミックスインと通常の継承の違い
CRTPミックスインが通常の継承と異なるのは、基底クラスから派生クラスへ降りて呼び出せる点です。通常の基底クラスは、仮想関数なしでは派生型にのみ存在するメソッドを呼び出せません。
ポリシーベース設計
ミックスインによってポリシーベース設計が可能になります。std::vectorがアロケーターのポリシーを受け取るのと同様に、どのミックスインを継承するかを選んで、コンパイル時に動作を選択します。
順序とレイアウト
ミックスインを積み重ねる場合、基底クラスの順序が構築順序とオブジェクトのメモリレイアウトに影響します。正しさが順序に左右されないよう、ミックスインは独立させてください。
- 基底クラスの宣言順に構築される
- 互いに依存するミックスインは避ける
等価比較ミックスインを追加する
ここでは、ホストの等価比較からoperator!=をミックスインで生成しています。
#include <iostream>
template <typename T>
struct EqualityMixin {
friend bool operator!=(const T& a, const T& b) { return !(a == b); }
};
struct P : EqualityMixin<P> {
int x;
P(int v) : x(v) {}
friend bool operator==(const P& a, const P& b) { return a.x == b.x; }
};
int main() {
std::cout << (P(1) != P(2)) << "\n";
return 0;
}条件付きミックスイン
継承とstd::conditionalを組み合わせ、実際のミックスインと空のミックスインのどちらかを選ぶことで、条件が成立する場合にのみミックスインを含めることができます。
空の基底クラス最適化
データメンバーを持たないミックスインは、空の基底クラス最適化の恩恵を受けます。コンパイラーはそれらのために追加のバイトを割り当てないため、状態を持たないミックスインを多数積み重ねても、サイズの面ではコストがかかりません。
#include <iostream>
template <typename T>
struct A {};
template <typename T>
struct B {};
struct C : A<C>, B<C> {
int x;
};
int main() {
std::cout << (sizeof(C) == sizeof(int)) << "\n";
return 0;
}ミックスインを使う場面
ミックスインは、多くのクラスで同じ直交的な動作(ロギング、カウント、比較)が必要な場合に特に有効です。それぞれの動作を1か所にまとめ、実行時コストなしで組み合わせられます。
確認問題
CRTPミックスインについての理解度を確認してください。
まとめ
CRTPを使ったミックスインの構築方法を学びました。
- ミックスインは直交的な動作をコンパイル時に組み合わせる
- ホストクラスへ降りて呼び出せるほか、状態も保持できる
- 状態を持たないミックスインは、空の基底クラス最適化によってコストがかからない
- ロギング、比較、カウントのポリシーに適している
よくある質問
「CRTPによるミックスイン」レッスンは無料ですか?
はい。「CRTPによるミックスイン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C++ Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C++ Academyコースには全4レッスンが含まれています。
「CRTPによるミックスイン」で何を学びますか?
コンパイル時に振る舞いを組み合わせます ブラウザで直接実行するハンズオンコードでC++ Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C++ Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC++ Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「CRTPによるミックスイン」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC++ Academyレッスンでコードを書いて実行できますか?
はい。すべてのC++ Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- CRTPイディオム
- 静的ポリモーフィズム
- CRTPによるミックスイン
- CRTPを使うタイミング