【问题标题】:Class with templated member function, is the same class?具有模板化成员函数的类,是同一个类吗?
【发布时间】:2012-06-20 14:33:41
【问题描述】:

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

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

提前致谢。

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    所有的重载都是编译器为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.
    }; 
    

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

    【讨论】:

      【解决方案2】:

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

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

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

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

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

      它们被编译器实例化(编译成二进制代码),where 不是一个正确的问题(除了二进制)。成员函数的地址只能从 class 中获取,而不能从任何实例中获取,正如我已经提到的,成员函数是 per-type,而不是 per-instance。

      【讨论】:

      • "成员函数的地址只能从类中获取",假设我们想使用一个float函数作为回调,我们可以定义一个float成员函数指针:void (Foo::*CallBack)(float) = &amp;Foo::f&lt;float&gt;; 并让它指向 foo 的任何 f 成员,因为它们都有带有 float 的 af 成员(由于 @Akanksh 提到的 代码膨胀):Foo &amp;foo = V.at(0); (foo.*CallBack)(12.34f);
      • @PaperBirdMaster:我不明白这个问题。指向成员的指针必须从类中获得&amp;Foo::f&lt;float&gt;(注意Foo,它必须应用到实例:(foo.*Callback)。什么是你的担心?
      • 这是一个误会,我很傻>_类本身获得了函数指针,它被用于实例。误解来自我对模板化成员函数的错误概念。我试图证明(实际上是对我自己)所有实例都有所有的重载,指向一个“概念上”实例不能有的函数。
      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

        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 结构有两个“从开始”创建的函数成员,而在模板化版本中,您不知道在编译时之前创建了哪些成员。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-08
        • 1970-01-01
        • 2012-04-03
        • 2018-02-14
        • 2018-12-16
        • 1970-01-01
        相关资源
        最近更新 更多