我对模板化成员函数有点困惑,让我们假设我们有一些带有模板化成员函数的奇怪结构,如下所示:
struct Foo
{
template <typename T> void f(T t) {};
};
然后,我们将该结构存储到一些标准容器中:
std::vector<Foo> V;
V.push_back(Foo());
V.push_back(Foo());
V.push_back(Foo());
V.push_back(Foo());
下一步;让我们调用模板化成员函数的不同实例:
V.at(0).f<int>(1);
V.at(0).f<char>(2);
V.at(1).f<float>(3.4f);
V.at(2).f<double>(5.6);
V.at(3).f<long>(7);
最后,问题是:
¿Foo类的所有实例都来自同一个类?似乎答案是肯定的但是......第一个Foo实例最后有两个f成员函数的重载:
[0] Foo::f(int t);
[0] Foo::f(char t);
另一方面,其他Foo实例似乎只有一个版本的f函数。因此,由于成员函数的不同,每个实例的基本类型显然不同。
[1] Foo::f(float t);
[2] Foo::f(double t);
[3] Foo::f(long t);
¿f函数是实例化的吗?显然我们只能从第一个Foo实例获取Foo :: f(int t)函数的地址,因为该函数只属于该实例;其他功能相同。
提前致谢。
答案 0 :(得分:7)
所有重载都是由Foo::f
成员函数的编译器生成的,您可以将其视为已经手动写出所有这些重载。
重载生成(模板实例化)不是基于实例的,而是基于类的,类本身获取所有模板实例化splatted(就像编写了不同类型的所有重载一样)在类的主体中,对于给定的类型T)
所以这就是你的情况:
struct Foo
{
template <typename T> void f(T t) {};
};
将成为(概念上)
struct Foo
{
void f<int>(int t) {};
void f<char>(char t) {};
void f<float>(float t) {};
...
/// all the other uses of f with different types, anywhere in your code.
};
这是人们反对模板的原因之一,它被称为“代码膨胀”。
答案 1 :(得分:1)
¿Foo类的所有实例都来自同一个类?似乎答案是肯定的
是的,所有实例都属于同一类型。
但是......第一个Foo实例最后有两个f成员函数的重载
重载是per-type,而不是每个实例。无论实例化模板函数的对象是什么,该类型都将包含所有重载。
¿f函数在哪里实例化?显然我们只能从第一个Foo实例获取Foo :: f(int t)函数的地址,因为该函数只属于该实例;其他功能相同。
它们被编译器实例化(编译成二进制代码),其中不是一个正确的问题(除了在二进制文件中)。成员函数的地址只能从类获得,而不是从任何实例获得,正如我已经提到的,成员函数是per-type,而不是每个类型的实例。
答案 2 :(得分:0)
所有Foo
实例都具有所有重载,并且它们都是相同的类型,因为Foo
本身不是模板类。
在编译时,在目标代码中实例化函数。
答案 3 :(得分:0)
让我们看一个不同的例子:
struct Foo
{
void f(int);
void f(double);
};
// ...
Foo foo1, foo2;
foo1.f(3);
foo2.f(3.0);
这与您自己的版本完全类似,只是编译器不会创建方法f()
的新实例化。我们只在f(int)
上调用方法foo1
,我们只在f(double)
上调用方法foo2
。你认为他们现在有不同的类型,只是因为我们不会在每个实例上调用所有方法吗?
方法与类相关联,而不与实例相关联。代码生成的所有方法重载都是类Foo
的方法,并且所有实例都具有相同的类型。