【问题标题】:What is compile-time polymorphism and why does it only apply to functions?什么是编译时多态性,为什么它只适用于函数?
【发布时间】:2010-12-25 06:51:35
【问题描述】:

什么是编译时多态性,为什么它只适用于函数?

【问题讨论】:

  • 我的教科书有一些矛盾之处,目前还没有老师的回答。我想 SO 可能会有答案。
  • 你能描述一下你看到的矛盾吗? - 也许这有助于回答您的实际问题。
  • 我的教科书有歧义,或者我误解了我在读什么,所以我想我在这里问。
  • 我想课程是我能想到的唯一其他事情,但这似乎不对。
  • @nmr 我同意。编译器确实会根据我们提供的类型 T 选择要实例化哪个版本的类,这是否意味着类模板也表现出编译时多态行为?

标签: c++ polymorphism compile-time


【解决方案1】:

很久以前,“编译时多态性”意味着函数重载。它仅适用于函数,因为它们都是你可以重载的。

在当前的 C++ 中,模板改变了这一点。 Neil Butterworth 已经举了一个例子。另一个使用模板专业化。例如:

#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;
}

这应该产生420""(一个空字符串)——我们得到了一个对每种类型都有不同作用的结构。

这里我们有类而不是函数的“编译时多态性”。我想如果你想争论这一点,你可以声称这至少部分是构造函数(一个函数)在至少一种情况下的结果,但 my_template 的专用版本甚至没有 一个构造函数。

编辑:至于为什么这是多态性。我将“编译时多态性”放在引号中是有原因的——它与普通的多态性有些不同。尽管如此,我们得到的效果类似于我们期望从重载函数中获得的效果:

int value(int x) { return 0; }
long value(long x) { return 42; }

std::cout << value(1);
std::cout << value(1L);

函数重载和特化产生了类似的效果。我同意“多态性”是否适用于任何一个问题都存在一些问题,但我认为它对一个和另一个同样适用。

【讨论】:

  • 编译时多态这个术语有点含糊,你能详细说明一下你认为你的例子是多态的吗?无论这是否是多态性,我仍然跨界。我可能会争辩说,这个例子的多态部分是运算符&lt;&lt;std::ostream 的重载。是否应该将模板实例化视为一种以类型为参数的抽象函数?
  • 您如何认为多态性不适用?多态性意味着“多种形式”。在您的示例中(以及在函数重载中),相同的函数选择器会导致函数的几种不同“形式”。
【解决方案2】:

只适用于函数的是模板参数推导。如果我有一个函数模板:

template <typename T> 
void foo(T &t);

然后我可以做int a = 0; foo(a);,这将等同于int a = 0; foo&lt;int&gt;(a);。编译器发现我的意思是foo&lt;int&gt;。至少,它应该使用foo&lt;int&gt; - 如果这不是我的意思,那么我倒霉,我可以写foo&lt;unsigned int&gt;(a); 或其他什么。

但是,如果我有一个类模板:

template <typename T>
struct Foo {
    T &t;
    Foo(T &t) : t(t) {}
    T &getT() { return t; }
};

那我不能int a = 0; Foo(a).getT();。我必须指定Foo&lt;int&gt;(a)。不允许编译器解决我的意思是Foo&lt;int&gt;

所以你可能会说类模板比函数模板“更少多态”。多态性通常意味着您不必编写代码来明确对象的类型。函数模板允许这样做(在这种特殊情况下),而类模板则不允许。

至于为什么会这样-标准是这样说的,我不知道为什么。通常的怀疑是 (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被实例化了。

【讨论】:

  • 我认为根据标准委员会的说法它很有用,因为他们选择通过实现 std::mem_fun / std::mem_fun_t 来解决这个特定问题。基本上他们所做的是: template std::mem_fun_t<_tx> mem_fun(const _Tx &t) { return std::mem_fun_t<_tx>(t); }
  • 是的,迭代器适配器也是如此:back_inserter 返回一个back_insert_iterator。当我说“和”时,我的意思是“这是清单”,而不是“这些都是真的”。我认为(a)也不太可能,所以要么是(c)要么“没有充分的理由”。
【解决方案3】:

对于编译时多态性,通常意味着您可以拥有多个同名的函数,编译器将在编译时根据参数选择使用哪一个:

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 被称为重载。各种类型的模板实例化也可以称为编译时多态性。

【讨论】:

  • 类模板呢?编译器确实会根据我们提供的类型 T 来选择要实例化哪个版本的类,这是否意味着类模板也表现出编译时多态行为?
  • @captonssj,我会说是的,他们会。
  • 这更像是一个运行时多态的例子吗?我想我对两者的区别感到困惑,即运行时多态性和编译时多态性......叹息......
  • 我的例子中的代码是编译时,即应该调用哪个重载函数是在编译时确定的。
【解决方案4】:

编译时多态性是指 C++ 模板编程的术语。例如,在编译时,您可以根据 std::vector 包含的内容确定其实际类型:

std::vector <int> vi;
std::vector <std::string> vs;

我不知道你为什么认为它仅限于功能。

【讨论】:

  • 主要是因为我在课堂上被告知它仅限于函数。我不完全理解为什么它仅限于功能。
  • @Neil 我同意你的看法。类模板似乎也表现出编译时多态性。
【解决方案5】:

编译时多态适用于函数和运算符重载。

阅读此http://cpp-tutorial.cpp4u.com/OOP_polymorphism.html

【讨论】:

    猜你喜欢
    • 2011-09-04
    • 1970-01-01
    • 2014-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-05
    相关资源
    最近更新 更多