从(模板)基类内部调用虚拟成员函数

时间:2011-11-11 08:16:54

标签: c++ templates gcc polymorphism

假设我有以下内容:

#include <iostream>
#include <string>

template<class T>
class base
{
public:
    void print()
    {
        T t = get();
        std::cout << t << std::endl;
    }

    virtual T get() const
    {
        // assumes T can be constructed from, say, -1
        T t = -1.0;
        return t;
    }
};

class derived : public base<std::string>
{
public:
    virtual std::string get() const
    {
        // this is a silly example, but one can
        // imagine that what we return here could
        // depend on data members of derived
        return "this is a string";
    }
};

int main()
{
    derived d;
    d.print();

    return 0;
}

在我看来,d.print()应该调用derived::get(),因为get()是虚拟的。但是,我收到编译错误,说我无法将string初始化为-1.0,这意味着当我调用{{1}时编译器正试图调用base::get() }。发生了什么事?

4 个答案:

答案 0 :(得分:6)

  

但是,我收到编译错误,说我无法将字符串初始化为-1.0,这意味着当我调用d.print()时,编译器正在尝试调用base :: get()。 / p>

不,编译器错误意味着编译器正在尝试实例化必须所做的base<std::string>::get(),因为derived使用base<std::string>作为基类。仅仅因为你没有调用函数并不意味着你不能。您仍然可以直接致电base<std::string>::get()

您实例化base<std::string>并将其用作基类。由于base<std::string>::get()是一个虚函数,因此您将base<std::string>用作基类这一事实被视为“使用”。由于它正在使用中,因此必须进行实例化。因此编译器必须并将尝试编译该函数。

由于std::string不能从float隐式构造,因此编译器会因失败的模板替换而出错。

答案 1 :(得分:4)

当你隐式实例化一个类模板时,只会那些使用的成员函数被实例化,正如你已经知道的那样,否则你就不会问这个。问题是标准中 use 的定义可能不完全符合您的预期,特别是,任何虚拟函数使用如果它不是

  

§3.2p2[...]虚拟成员函数如果不是纯粹的,则会被使用。[...]

这意味着即使您的代码没有显式调用它,也会使用base::get,因此编译器将隐式实例化它并触发您看到的编译器错误。

答案 2 :(得分:1)

T t = -1.0;

当然,如果Tstd::string,则无法编译。它与get virtual无关,而且在运行时将调用哪个函数。运行时在代码编译成机器代码后出现,而您的代码甚至无法编译。

你为什么不这样做:

T t = T(); 

同样需要T才能拥有默认构造函数。

答案 3 :(得分:1)

问题出在这个方法中(其中T = std :: string):

virtual T get() const
{
    // assumes T can be constructed from, say, -1
    T t = -1.0;
    return t;
}

编译器是对的。您不能使用double值初始化std :: string。