g++ でビルドしたら “sorry, unimplemented: non-trivial designated initializers not supported” とエラーになる話

永いことソフトウェア開発の現場におりますが、それでも知らなかったことに出くわすことがすくなくありません.

きょうはそんな話.

C99 でサポートされている指示初期化子 (designated initializers) のC++での扱いについて.

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++の構造体,クラスの初期化はこちらの文献を参考にしました.

では.