volatileとコピー・代入
C++でvolatile修飾された構造体はものすごく扱いにくい。
typedef struct { int x; } X; int main() { X n_1; volatile X v_1; /* コピー(代入ではない)*/ X n_2 = n_1; /* OK */ X n_3 = v_1; /* エラー */ volatile X v_2 = n_1; /* OK */ volatile X v_3 = v_1; /* エラー */ /* 代入 */ n_2 = n_1; /* OK */ n_3 = v_1; /* エラー */ v_2 = n_1; /* エラー */ v_3 = v_1; /* エラー */ return 0; }
このコードはC++では大量のエラーが出るが、C言語ではコンパイルが通る。おそらくCとC++の非互換性の一つではないかと思われる。
原因はC++のデフォルトコピーコンストラクタ・代入演算子の定義にある。
X::X(const X&) X& X::operator=(const X&)
これらの関数の引数になったりthisポインタになったりするときに、volatileがついていると暗黙の型変換ができないのでエラーとなる。
これはC言語の型変換が甘いことや、C++の型チェックが厳しすぎることが原因ではない。C++のデフォルトの関数が足りないことが原因である。
一番望ましい対策は標準でこれらを定義することだと思う。
X::X(const volatile X&) volatile X& X::operator=(const volatile X&) volatile X& X::operator=(const volatile X&) volatile X& X::operator=(const X&) volatile
パフォーマンスを考慮しないなら下の二つは必要ない。
volatileはあまり使われていないのだろう。*1この件に関して見つかったのは2chのレスぐらいだった。*2しかしC++はC言語との互換性を重視しているはずなので、ぜひとも検討してほしい。
ちなみに、volatileが絡む例で一つだけコンパイルが通っている行がある。
volatile X v_2 = n_1; /* OK */
v_2はvolatileオブジェクトだが、構築中であるのでエラーにはならない。コンストラクタでthisポインタが修飾されることはないので、例えば次のようなコンストラクタは定義できない。
X::X() volatile { } // エラー
さて、ユーザレベルで対応するにはキャストするのが手っ取り早い。
template <class T> inline void Assign(volatile T& dest, const volatile T& src) { const_cast<T&>(dest) = const_cast<const T&>(src); }
結果は保証されないだろうが、現実的にはあまり問題は無いと思う。
*1:組み込みでは大量にvolatileをばらまくことがよくある。
*2:http://pc11.2ch.net/test/read.cgi/tech/1234420483/182 海外のコミュニティにはありそうな気がする。