テンプレート関数・クラスの明示的実体化について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
zack

テンプレート関数・クラスの明示的実体化について

#1

投稿記事 by zack » 4年前

テンプレート関数・クラスの定義(ヘッダファイル)と実装(CPPファイル)を分離する方法として
明示的実体化(インスタンス化)があります。

多くのC++解説サイトでは、この明示的実体化はCPPファイル側に記述してあります。

しかし、例えばクラスのメンバ関数ごとに実装のCPPファイルを作成する場合、
すべてのCPPファイルに明示的実体化が必要になります。

一方で明示的実体化をヘッダに記述すると、その記述のみで
コンパイル可能です。

ヘッダで明示的実体化することは、なにか問題があるのでしょうか?

よろしくお願いします。

コード:

//hoge.h

template <int a>
class hoge{

 public:
  int f();
  int g();
};

//template class hoge<1>; これがあるとOK

コード:

//f.cpp

#include "hoge.h"

template <int a>
int hoge<a>::f(){return 1;};

template class hoge<1>; //ないとエラー

コード:

//g.cpp

#include "hoge.h"

template <int a>
int hoge<a>::g(){return 2;};

template class hoge<1>; //ないとエラー

コード:

//main.cpp

#include <iostream>
#include "hoge.h"

using namespace std;

int main(){

  class hoge<1> h;
  
  cout<<h.f()<<endl;
  cout<<h.g()<<endl;

  return 0;
}

アバター
tk-xleader
記事: 153
登録日時: 9年前
連絡を取る:

Re: テンプレート関数・クラスの明示的実体化について

#2

投稿記事 by tk-xleader » 4年前

 ヘッダに強制実体化のコードを書くと、全てのファイルでテンプレートを実体化するということになりますが、時間的にも空間的にもコンパイル効率が悪くなります。

 C++11で、extern templateという文法が導入され、他の翻訳単位にテンプレートの実体が存在することを宣言することができるようになりましたので、

extern template class hoge<1>;

とヘッダに書いた上で、いずれか1つのファイルで実体化するという改善方法が考えられます。

zack

Re: テンプレート関数・クラスの明示的実体化について

#3

投稿記事 by zack » 4年前

返信ありがとうございます。

しかし、ご教示いただいた改善方法ではうまくいきませんでした。

hoge.hでexternをつけた明示的実体化を行い、f.cppの明示的実体化を削除したところ
hoge<1>::f()に対してリンクエラーが発生しました。

これは、クラスの明示的実体化を行う際には、翻訳単位内に定義が存在するメンバ関数(例えばg.cpp内でのg())しか
実体化されないからだと推測されれます。

よろしくお願いします。

アバター
tk-xleader
記事: 153
登録日時: 9年前
連絡を取る:

Re: テンプレート関数・クラスの明示的実体化について

#4

投稿記事 by tk-xleader » 4年前

 全ての翻訳単位で実体化することによって不都合があるのかといえば、ODR原則さえ守っていれば基本的には問題ないのですが、やはり不必要に実体化するのは時間的・空間的に非効率ではあります。
 したがって、明示的実体化は必要な範囲にとどめ、それ以外はextern templateで対応するのが一番いいとは思います。hoge<1>::fの定義があるf.cppでも実体化すれば大丈夫だろうと思います。少なくともmain.cppではhoge<1>の実体化が必要ではないはずなので。

sleep

Re: テンプレート関数・クラスの明示的実体化について

#5

投稿記事 by sleep » 4年前

template は、コンパイル単位上に実装が存在しないと インスタンス化はできません。
extern template は、あくまでコンパイル単位上に実装が存在する状態が前提です。その状態のときに「インスタンス化させない」という選択ができる機能です。
hoge.h に extern template を記述し、f.cpp、g.cpp から template class hoge<1>; を削除した場合、コンパイル単位である f.cpp と g.cpp では それぞれ 実装を「インスタンス化させない」ことを選択したことになります。
更にその場合、hoge.h、main.cpp のどちらのファイル上にも実装は存在しないので、その状態からはどう足掻いても hoge<a>::f() と hoge<a>::g() の実装をインスタンス化させることはできません。


あくまで定義と実装を別ファイルに分けることだけを目的としているのなら、
boostライブラリの実装で実践されている手法が情報として役に立つかもしれません。

hoge.h

コード:

#pragma once

template <int a>
class hoge{
 
 public:
  int f();
  int g();
};

#include "f.hpp"
#include "g.hpp"
f.hpp

コード:

template <int a>
int hoge<a>::f(){return a;};
g.hpp

コード:

template <int a>
int hoge<a>::g(){return a*2;};
test.cpp

コード:

//g++ -std=c++14 -o test test.cpp
#include <iostream>
#include "hoge.h"
using namespace std;

int main()
{
	hoge<1> a;
	cout << a.f() << endl;
	cout << a.g() << endl;

	hoge<2> b;
	cout << b.f() << endl;
	cout << b.g() << endl;

	hoge<2> c;
	cout << c.f() << endl;
	cout << c.g() << endl;
	cin.ignore();
}
最後に
zack さんが書きました: ヘッダで明示的実体化することは、なにか問題があるのでしょうか?
実際に問題となったときに考えてみてはどうでしょう。

zack

Re: テンプレート関数・クラスの明示的実体化について

#6

投稿記事 by zack » 4年前

>>tk-xleader

返信ありがとうございます。
確かにexternを使用したほうが効率的ではありますね。
しかし他の引数を使いたくなったとき(例えばhoge<2>)に
全てのファイルに追記するのは手間なので、今回は効率には目をつぶることにします。

>>sleep

返信ありがとうございます。
ファイル分割をしたい最大の理由は分割コンパイルをすることだったので、
ヘッダからCPPファイルをインクルードする手法では実現できず、明示的実体化を選択しました。
おっしゃるとおり、取りあえずやってみて、問題が発生したときに考えることにします。

閉鎖

“C言語何でも質問掲示板” へ戻る