コンパイル時のポリモーフィズムとは何ですか?なぜそれが関数にのみ適用されるのですか?
5 に答える
昔、「コンパイル時のポリモーフィズム」は関数のオーバーロードを意味していました。オーバーロードできるのは関数だけなので、関数にのみ適用されます。
現在のC++では、テンプレートがそれを変更します。ニールバターワースはすでに1つの例を挙げています。もう1つは、テンプレートの特殊化を使用します。例えば:
#include <iostream>
#include <string>
template <class T>
struct my_template {
T foo;
my_template() : foo(T()) {}
};
template <>
struct my_template<int> {
enum { foo = 42 };
};
int main() {
my_template<int> x;
my_template<long> y;
my_template<std::string> z;
std::cout << x.foo << "\n";
std::cout << y.foo << "\n";
std::cout << "\"" << z.foo << "\"";
return 0;
}
これにより、、、および(空の文字列)が生成42
さ0
れ""
ます。タイプごとに異なる動作をする構造体を取得しています。
ここでは、関数の代わりにクラスの「コンパイル時ポリモーフィズム」があります。要点を議論したいのであれば、これは少なくとも部分的には少なくとも1つのケースでコンストラクター(関数)の結果であると主張できますが、の特殊バージョンにはコンストラクターさえありmy_template
ません。
編集:これがポリモーフィズムである理由について。「コンパイル時のポリモーフィズム」を引用符で囲んでいるのには、通常のポリモーフィズムとは多少異なります。それでも、関数のオーバーロードから期待するのと同様の効果が得られます。
int value(int x) { return 0; }
long value(long x) { return 42; }
std::cout << value(1);
std::cout << value(1L);
関数のオーバーロードと特殊化は同様の効果をもたらします。「ポリモーフィズム」がどちらにも当てはまるかどうかは疑問の余地があると思いますが、どちらにもほぼ同じように当てはまると思います。
コンパイル時のポリモーフィズムでは、通常、同じ名前の複数の関数を使用でき、コンパイラはコンパイル時に引数に応じて使用する関数を選択するという事実を意味します。
void foo(int x);
void foo(float y);
//Somewhere else
int x = 3;
foo(x); //Will call first function
float y = 2;
foo(y); //Will call second function
関数foo
はオーバーロードされていると言われます。さまざまなタイプのテンプレートのインスタンス化は、コンパイル時のポリモーフィズムとも呼ばれます。
コンパイル時のポリモーフィズムは、C++テンプレートプログラミングを指す用語です。たとえば、コンパイル時に、std::vectorの実際のタイプをその内容によって決定します。
std::vector <int> vi;
std::vector <std::string> vs;
機能に限定されていると思う理由がわかりません。
関数にのみ適用されるのは、テンプレートパラメータの推定です。関数テンプレートがある場合:
template <typename T>
void foo(T &t);
次に、を実行できます。int a = 0; foo(a);
これは、と同等になりint a = 0; foo<int>(a);
ます。コンパイラは、私が意味することを理解しfoo<int>
ます。少なくとも、それを使用する必要があることがわかりますfoo<int>
-それが私が意図したものでない場合は、私にとって不運であり、私は何かを書いた可能性がありfoo<unsigned int>(a);
ます。
ただし、クラステンプレートがある場合:
template <typename T>
struct Foo {
T &t;
Foo(T &t) : t(t) {}
T &getT() { return t; }
};
それなら私にはできませんint a = 0; Foo(a).getT();
。指定する必要がありますFoo<int>(a)
。コンパイラは、私が意味することを理解することを許可されていませんFoo<int>
。
したがって、クラステンプレートは関数テンプレートよりも「ポリモーフィックが少ない」と言えます。ポリモーフィズムとは、通常、オブジェクトのタイプを明示的にするためにコードを記述する必要がないことを意味します。関数テンプレートはそれを許可しますが(この特定の場合)、クラステンプレートは許可しません。
なぜそうなるのかというと、標準ではそう言われていますが、理由はわかりません。通常の容疑者は、(a)実装が難しすぎる、(b)標準委員会の意見では役に立たない、または(c)言語のどこかで矛盾やあいまいさを生み出すことです。
ただし、クラスを使用して他の種類のポリモーフィズムを実行することはできます。
template <typename T>
struct Foo {
T &t;
Foo(T &t): t(t) {}
void handleMany(int *ra, size_t s) {
for (size_t i = 0; i < s; ++i) {
t.handleOne(ra[i]);
}
}
};
これは通常、コンパイル時のポリモーフィズムとも呼ばれます。これは、テンプレートの作成者に関する限り、t.handleOne
何でもかまいません。必要に応じて、Fooがインスタンス化されるときにコンパイルの「後で」解決されるためです。
コンパイル時のポリモーフィズムは、関数と演算子のオーバーロードに適用されます。