- 2009年3月19日 21:44
- C++
C++ でメンバ関数テンプレートを仮想関数にしようとして、コンパイラに怒られた経験がある人は多いと思う。
class Hoge {
public:
template <typename T>
virtual void Func(const T& in) {} // メンバ関数テンプレートは仮想関数にできない
};
こういう場合、クラスを丸ごとテンプレート化してしまえば何とかなる場合が多いので、どうしてもという時は設計を見直すことになるだろう。こうすれば仮想関数でテンプレート引数の型が使えるようになる。
template <typename T> // テンプレートクラスにすれば、仮想関数で T が使える
class Hoge {
public:
virtual void Func(const T& in) {}
};
仮想関数ではないメンバ関数テンプレートや、テンプレートではない仮想関数は可能なのに、この2つが合わさるとダメになってしまうのは何故なのだろう。短い答えは「そういう仕様だから」だ。C++ の標準規格書には、はっきり「ダメ」と書いてある。
A member function template shall not be virtual. [Example:
template <class T> struct AA {
template <class C> virtual void g(C); // error
virtual void f(); // OK
};
--end example]
ISO/IEC 14882:2003, 14.5.2 Member templates - 3
でも、これで終わってしまったら面白くないので、もう少し掘り下げてみよう。何故こういう仕様になったのだろうか。テンプレートクラスが仮想メンバ関数を持てるならば、仮想テンプレートメンバ関数を許してくれても良いではないか。
色々調べてみた結果、満足のいく答えは Stroustrup 氏の著書「プログラム言語 C++ 第3版」で見つけることができた。灯台下暗しとはこのことだ。
メンバテンプレートは、virtual 宣言できない。たとえば次の通り。
class Shape {
// ...
template<class T> virtual bool intersect(const T&) const = 0; // エラー: virtual テンプレート
};
この例は、文法から逸脱しているものとして扱わなければならない。これを認めると、仮想関数を実装するために伝統的に使われてきた仮想関数テーブルテクニック(§2.5.5)が使えなくなる。誰かが新しい引数型を指定して intersect() を呼び出すたびに、リンカは Shape クラスの仮想テーブルに新しいエントリを追加しなければならなくなってしまう。
プログラム言語 C++ 第3版, 13.6.2 メンバテンプレート
なるほど、納得。全ての翻訳単位をコンパイルしてみないと、仮想関数テーブルが一意に定まらないというわけか。
ところで、何故「仕様だから」で済むような話にここまでこだわるのか。それは C# では仮想メンバ関数にジェネリクス(template のようなもの)が使えると聞いたからだ。C++ 以外の言語はサッパリなので、これを機に少し調べてみようと思ったのだが、その前に出来ない理由を明確にしておこうと思った次第。
C# はこの問題をどうやって解決しているのだろう......と、考えているうちに、なんとなく答えが分かったような気がする。C#(に限らず .NET 系全般)なら Just In Time でコンパイルしてしまえば良いではないか。無いものは作ればいい。リンカが困ったらコンパイル。たぶん、これだな。
- Newer: コレクションの永続化
- Older: Blender で favicon
コメントを表示する前に、このブログのオーナーによる承認が必要になることがあります。コメントが表示されない場合は、承認されるまでしばらくお待ちください。