LoginSignup
2
0

More than 5 years have passed since last update.

g++: テンプレートクラスの明示的インスタンス化で `=default` した関数が生成されないバグ

Last updated at Posted at 2017-12-07

問題となるケース

以下のようなコードをコンパイルする。

foo.h
#include <vector>

template <typename T>
struct Foo {
#ifdef USE_DEFAULT
  Foo() = default;
#else
  Foo() {}
#endif
  std::vector<int> x;
};
foo.cc
#include "foo.h"
template struct Foo<int>;

Foo() {} のほうだと

$ g++ -std=c++11 -c foo.cc 
$ nm -C foo.o | grep Foo
0000000000000000 W Foo<int>::Foo()
0000000000000000 W Foo<int>::Foo()
0000000000000000 n Foo<int>::Foo()

のようにコンストラクタのコードが生成されていることがわかる。
一方、 Foo() = default; だと

$ g++ -std=c++11 -c -DUSE_DEFAULT foo.cc 
$ nm foo.o 
(出力なし)

のように生成されない。

これは g++ のバグのようだ → https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57728

C++11 以降だと、テンプレートの明示的インスタンス化は、利用側での extern template と併せて使われることが多いと思うが、その場合このコンストラクタの定義が生成されていないため、リンク時にエラーになる。

確認は以下の g++ 5.4.0 で行ったが、最新の 7.2.0 でも同様の結果だったので直っていないようだ。


$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

インライン ?

extern template でインスタンス化を抑制しているからといって、インライン化しないわけではない。
インライン化された場合にはこのバグは問題にならない。

上の例の foo.h を利用する以下のコードを考える。

main.cc
#include "foo.h"

extern template class Foo<int>;

int main() {
  Foo<int> foo;
}

最適化オプションなしでコンパイルすると以下のようになる。

$ g++ -std=c++11 -c -DUSE_DEFAULT main.cc
$ nm -C main.o  | grep Foo
                 U Foo<int>::Foo()
0000000000000000 W Foo<int>::~Foo()
0000000000000000 W Foo<int>::~Foo()
0000000000000000 n Foo<int>::~Foo()

コンストラクタのシンボルを参照している。しかしこのコンストラクタは生成されていないから、リンク時にエラーとなる。

$ g++ -std=c++11 -DUSE_DEFAULT main.cc foo.cc
/tmp/ccQHJquT.o: In function `main':
main.cc:(.text+0x1f): undefined reference to `Foo<int>::Foo()'
collect2: error: ld returned 1 exit status

一方、 -O2 をつけてみると

$ g++ -std=c++11 -c -DUSE_DEFAULT -O2 main.cc
$ nm -C main.o  | grep Foo
(出力なし)
$ g++ -std=c++11 -DUSE_DEFAULT -O2 main.cc foo.cc
$ echo $?
0

と問題なくリンクできる。 インライン化により、コンストラクタ呼び出しが消滅しているためである。

対処法

  • 明示的インスタンス化するクラスでは = default を使わない
  • extern template を使わない

のような消極的な方法しか思いつかない…。

まとめ

C++ テンプレートクラスの明示的インスタンス化の際、 =default で定義された関数が生成されない g++ のバグについて書きました。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0