永いことソフトウェア開発の現場におりますが、それでも知らなかったことに出くわすことがすくなくありません.
きょうはそんな話.
C99 でサポートされている指示初期化子 (designated initializers) のC++での扱いについて.
Table of Contents
C言語ならよくみる記述
Linux Kernel の中ではよく見られる構造体の初期化.
static struct xt_match socket_mt_reg[] __read_mostly = {
{
.name = "socket",
.revision = 0,
.family = NFPROTO_IPV4,
.match = socket_mt4_v0,
.hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN),
.me = THIS_MODULE,
},
こういう構造体の初期化を 指示初期化子 (designated initializers) と呼び, C99 より有効となりました.
以下の様な構造体を初期化したCコードは問題なくビルドできました.
struct sample
{
int a;
char b;
short c;
};
int main ()
{
struct sample s0 = { .a = 1, .b = 2, .c = 3};
struct sample s1 = { .a = 1, .b = 2};
struct sample s2 = { .b = 1, .c = 2};
return 0;
}
このコードを g++ version 7.3.0 でビルドしたところ
error sorry, unimplemented: non-trivial designated initializers not supported struct sample s2 = { .b = 1, .c = 2};
このようなエラーとなりました…
g++ version 7.3.0 は指示初期化子 (designated initializers)をサポートしていないと、つれない返事…
C++ には辛い…指示初期化子
指示初期化子 (designated initializers)はC99だけの機能で, C++にはサポートしていません.
調べてみるとこのような情報があり、整理してみると,
struct sample
{
int a;
char b;
short c;
};
上記の様な構造体を初期化する場合,
- C++ では構造体の先頭のメンバから指定することが必要.
上のコードであれば a から初期化する必要あり.
b から初期化するのは NG. - 途中のメンバが抜けるのはNG.
a の次に c を初期化するのはダメ. - 順番を守っていれば後ろのメンバの初期化を省略可能.
a, b と初期化すれば, c の初期化を省略可能. - 初期化の順番が逆なのはNG.
c, b, a という順盤で初期化はエラー.
ということが分かりました. 結構メンドクサイ…
g++ 8 以降はある程度条件が緩和される…が,
g++ version 8 以降は先の条件が幾分緩和されます.
- C++ では構造体の先頭のメンバから指定.
- 途中のメンバが抜けるのはNG.
という条件は外れます.
ただ、初期化のメンバの並びは逆になるのは NG です.
struct sample
{
int a;
char b;
short c;
};
int main ()
{
struct sample s0 = { .a = 1, .b = 2, .c = 3};
struct sample s1 = { .a = 1, .b = 2};
struct sample s2 = { .b = 1, .c = 2};
struct sample s4 = { .c = 1, .b = 2, .a = 3};
return 0;
}
上記の様なコードを g++ version 8.3.0 でビルドすると
struct_sample1.cpp: In function ‘int main()’: struct_sample1.cpp:13:46: error: designator order for field ‘sample::b’ does not match declaration order in ‘sample’ struct sample s4 = { .c = 1, .b = 2, .a = 3};
というエラーとなります.
やはり初期化のメンバの並びは逆になったときのビルドエラーは回避できません.
それでもメンバを逆順に初期化したい場合
諦めましょう(笑)
それでも頑なに構造体のメンバを逆順に初期化したい場合は
struct sample
{
int a;
char b;
short c;
};
int main ()
{
struct sample s0 = { .a = 1, .b = 2, .c = 3};
struct sample s1 = { .a = 1, .b = 2};
struct sample s2 = { .b = 1, .c = 2};
struct sample s3 = { .a = 1, .c = 2};
struct sample s4 {};
s4.c = 1;
s4.b = 2;
s4.a = 3;
return 0;
}
という個別に初期化するほかはないようです.
C++ は C の上位互換と思いがちなんですが、意外にもこういった落とし穴があるんですね.
構造体初期化についてはこちらの記事も参考になればと.
C++の構造体,クラスの初期化はこちらの文献を参考にしました.
では.