C++におけるPOD(Plain Old Data)とは、C言語のデータと互換を持つデータ構造のことです。
memcpyでデータをコピーできたりするものですが、それは本質的ではなく、とにかくPODの一番の意味はC言語のデータとbitレベルで完全に互換を持つということです。C++とC言語をまたぐプログラムがある場合、受け渡されるデータはPODでなければいけません。
会話中での出現例でいうと「あれ、ちゃんとPODで作っといてよ!」という具合に使います。
では、PODとは具体的にはどういうデータ構造でしょうか。
C互換データ=PODは、複雑なクラスレイアウトや、構築、コピー、ムーブなどのユーザ定義のセマンティクスを配慮することなく、”単なるデータ”として利用できるオブジェクトである。
__プログラミング言語C++第4版 8.2.6 POD
POD型の定義
- 標準レイアウト型(standard layout type)である
- トリビアル型(trivial type)である
この2つの定義を満たせば、それは紛れもないPOD型です。
簡単ですね!
それぞれの定義をさらに掘り下げます。
標準レイアウト型
- virtual関数を持たない
- virtual継承をしていない
- 参照メンバを持たない
- 非staticメンバに対して複数のアクセス指定子を持たない(重要!)
- 標準レイアウトでない型を継承していない
- 標準レイアウトでない非staticメンバを持たない
- 継承していてもいいが非staticメンバを持つクラスは継承ツリーの中で1つだけしかない
4番目の項目に注目してください。クラスの不定なメモリレイアウトでも触れたとおり、複数のアクセス指定子でメンバを定義してある場合、標準レイアウトの保証はなくなります。
トリビアル型
- トリビアルなデフォルトコンストラクタを持つ
- トリビアルなコピー動作およびムーブ動作をする
散々登場しているこの「トリビアルな○○」ってなんやねん!と、あなたは言うでしょう。分かっていますよ。
トリビアルなデフォルトコンストラクタを持つ、トリビアルなコピー動作、ムーブ動作をするとは、大雑把に言うとコンパイラ定義のこれらの動作をするということです。
例えば、コピーコンストラクタをユーザーが定義したら、それはもうトリビアルな型ではありません。
引数無しコンストラクタをユーザーが定義したら、コンパイラ定義のデフォルトコンストラクタが無くなるのでこれもトリビアルな型ではありません。
さあ、これで自分の書いたクラスがPODかどうか判断できるようになりました。でもこんなことは忘れてもいいのです。
あるクラスがPODかどうか、本当に知りたいのならstd::is_podを使いましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> class YourType { // 定義詳細 }; int main() { if (std::is_pod<YourType>::value) { // is_podを使ってPOD判定 std::cout << "PODだよ、Cと互換があるよ" << std::endl; } else { std::cout << "PODじゃない!絶対にCに渡すな!" << std::endl; } } |
(コンパイラベンダーの実装にバグが無い限り)正確にPODの判定をしてくれます。
小難しいことぁいいんだよ!という人へ。
それほど間違っていないPOD型
- 自分でコンストラクタとか定義してない
- virtualという文字列を打ってない
- 継承してない
- メンバ変数はprivate(もしくはpublic)にしか定義してない
- 組み込み型(int, char, float…etc)しかメンバに持ってない
とりあえず、これを抑えておけば大概はPODです。それでもPODじゃないデータ構造を定義してしまったら、逆にすごくね?と思うようにすると良いでしょう。
これはPOD。
1 2 3 4 5 |
struct Color { unsigned char red_; unsigned char green_; unsigned char blue_; }; |
これもPOD。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
class Vec2 { float x_; float y_; public: Vec2() = default; ~Vec2() = default; public: Vec2 operator +(const Vec2& rhs) const { Vec2 v; v.x_ = x_ + rhs.x_; v.y_ = y_ + rhs.y_; return v; } Vec2 operator -(const Vec2& rhs) const { Vec2 v; v.x_ = x_ - rhs.x_; v.y_ = y_ - rhs.y_; return v; } Vec2 operator *(const Vec2& rhs) const { Vec2 v; v.x_ = x_ * rhs.x_; v.y_ = y_ * rhs.y_; return v; } Vec2 operator /(const Vec2& rhs) const { Vec2 v; v.x_ = x_ / rhs.x_; v.y_ = y_ / rhs.y_; return v; } }; |
あと、おまけですがPODじゃない型のstaticメンバを持っていてもそのクラスのPOD性には影響ありません。
まとめ
- PODはC言語互換のデータ構造
- PODの定義は細かく定められている
- c++11ボーイエンドガールズはstd::is_pod<T>::valueで判定しよう