2

我对模板化成员函数有点困惑,假设我们有一些带有模板化成员函数的奇怪结构,如下所示:

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) 函数的地址,因为该函数只属于该实例;其他功能相同。

提前致谢。

4

4 回答 4

7

所有的重载都是编译器为Foo::f成员函数生成的,你可以认为它是手工写出来的。

重载生成(模板实例化)不是基于实例的,而是基于类的,类本身获取所有模板实例化(就像在类的主体中编写了所有不同类型的重载一样,对于给定的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.
}; 

这是人们反对模板的原因之一,它被称为“代码膨胀”。

于 2012-06-20T14:37:18.800 回答
1

¿ Foo 类的所有实例都来自同一个类?似乎答案是肯定的

是的,所有实例都属于同一类型。

但是...第一个 Foo 实例最后有两个 f 成员函数的重载

重载是每个类型,而不是每个实例。该类型将包含所有重载,无论模板函数在哪个对象上被实例化。

¿ f 函数在哪里被实例化?显然我们只能从第一个 Foo 实例中获取 Foo::f(int t) 函数的地址,因为该函数只属于该实例;其他功能相同。

它们由编译器实例化(编译成二进制代码),这不是一个合适的问题(除了二进制)。成员函数的地址只能从中获取,而不能从任何实例中获取,正如我已经提到的,成员函数是按类型的,而不是按类型的实例。

于 2012-06-20T14:39:29.377 回答
0

所有Foo实例都具有所有重载,并且它们都是同一类型,因为Foo它们本身不是模板类。

函数在编译时在目标代码中实例化。

于 2012-06-20T14:38:25.977 回答
0

让我们看一个不同的例子:

struct Foo
{
    void f(int);
    void f(double);
};

// ...
Foo foo1, foo2;
foo1.f(3);
foo2.f(3.0);

这与您自己的版本完全相似,只是编译器不会创建方法的新实例f()。我们只调用方法f(int)on foo1,而且我们只调用方法f(double)on foo2。您是否认为它们现在有不同的类型,只是因为我们没有在每个实例上调用所有方法?

方法与类相关联,而不是与实例相关联。您的代码生成的所有方法重载都是类的方法Foo,并且您的所有实例都具有相同的类型。

于 2012-06-20T14:40:11.040 回答