Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 具有模板成员函数的类,是同一个类吗?_C++_Templates - Fatal编程技术网

C++ 具有模板成员函数的类,是同一个类吗?

C++ 具有模板成员函数的类,是同一个类吗?,c++,templates,C++,Templates,我对模板成员函数有点困惑,让我们假设我们有一个带有模板成员函数的奇怪结构,如下所示: struct Foo { template <typename T> void f(T t) {}; }; 另一方面,其他Foo实例似乎只有一个版本的f函数。显然,由于成员函数的不同,每个实例的基类型是不同的 [1] Foo::f(float t); [2] Foo::f(double t); [3] Foo::f(long t); ?f函数的实例在哪里?显然,我们只能从第一个Foo实例

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

struct Foo
{
    template <typename T> void f(T t) {};
};
另一方面,其他Foo实例似乎只有一个版本的f函数。显然,由于成员函数的不同,每个实例的基类型是不同的

[1] Foo::f(float t);
[2] Foo::f(double t);
[3] Foo::f(long t);
?f函数的实例在哪里?显然,我们只能从第一个Foo实例获取Foo::f(int t)函数的地址,因为该函数只属于该实例;其他功能也一样

[1] Foo::f(float t);
[2] Foo::f(double t);
[3] Foo::f(long t);

提前感谢。

所有重载都是由编译器为
Foo::f
成员函数生成的,您可以将其视为已手动写出所有重载

重载生成(模板实例化)不是基于实例的,而是基于类的,类本身会将所有模板实例化飞溅(就像在类体中为给定类型T编写了不同类型的所有重载一样)

因此,在你的情况下:

struct Foo 
{     
   template <typename T> void f(T t) {}; 
}; 
structfoo
{     
模板空位f(T){};
}; 
将成为(概念上的)

structfoo
{     
void f(int t){};
空f(chart){};
void f(float t){};
...
///在代码中的任何地方,f的所有其他用法都具有不同的类型。
}; 

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

所有
Foo
实例都有所有重载,它们都是相同的类型,因为
Foo
本身不是模板类

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

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

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

但是。。。第一个Foo实例的末尾有两个f成员函数的重载

重载是按类型而不是按实例进行的。该类型将包含所有重载,而不管实例化模板函数的对象是什么

?f函数的实例在哪里?显然,我们只能从第一个Foo实例获得Foo::f(int t)函数的地址,因为该函数只属于该实例;其他功能也一样

[1] Foo::f(float t);
[2] Foo::f(double t);
[3] Foo::f(long t);

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

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

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

// ...
Foo foo1, foo2;
foo1.f(3);
foo2.f(3.0);
这与您自己的版本完全类似,只是编译器不创建方法
f()
的新实例化。我们只在
foo1
上调用方法
f(int)
,在
foo2
上调用方法
f(double)
。你认为他们现在有不同的类型吗,仅仅因为我们没有在每个实例上调用所有的方法


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

在我看来,这两个示例并不完全相似。只是因为在您的示例中,Foo结构有两个“从Begging”创建的函数成员,而在模板版本中,您不知道在编译时之前创建了哪些成员。“成员函数的地址只能从类中获得”,我们假设要使用浮点函数作为回调,我们可以定义一个float成员函数指针:void
(Foo::*CallBack)(float)=&Foo::f
并让它指向foo的任何f成员,因为它们都有一个带float的f成员(由于@Akanksh提到的代码膨胀):
foo&foo=V.at(0);(foo.*回调)(12.34f)@PaperBirdMaster:我不明白这个问题。指向成员的指针必须从类中获取:
&Foo::f
(注意
Foo
,并且必须应用于实例:
(Foo.*回调)
。你担心什么?这是一个误解,愚蠢的我><是的,你从类本身获得函数指针,并将其用于实例。误解来自于我对模板成员函数的错误概念。我试图(对自己)演示所有实例都具有所有重载,指向实例“概念上”不具有的函数。
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.
}; 
struct Foo
{
    void f(int);
    void f(double);
};

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