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

com*_*nie 4 c++ polymorphism gcc templates

假设我有以下内容:

#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;
}
Run Code Online (Sandbox Code Playgroud)

在我看来d.print()应该打电话,derived::get()因为它get()是虚拟的.但是,我收到一个编译器错误,说我无法初始化一个stringto -1.0,这意味着编译器base::get()在我调用时试图调用d.print().这是怎么回事?

Nic*_*las 6

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

不,编译器错误意味着编译器正在尝试实例化base<std::string>::get(),它必须这样做,因为derivedbase<std::string>用作基类.仅仅因为你没有调用函数并不意味着你不能.你仍然可以base<std::string>::get()直接打电话.

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

并且由于std::string无法从float隐式构造,因此编译器会因失败的模板替换而出错.

  • 关于@DavidRodríguez-dribeas评论:David忽略提及的一个重要观点(间接除外)是如果实例化该类型的对象(或从其派生的类型),则认为使用虚函数.(其动机是:编译器必须将其地址放在vtable中.)因此,虽然他对原始答案中的错误的评论是正确的,但快速测试可能会误导函数中的问题行; 基类中的函数被认为是"已使用",即使它在实际执行中永远不会被调用. (4认同)
  • @NicolBolas:据我了解规则,不应该修改另一个人的问题或答案来改变含义,而是修复拼写,格式化等问题.是的,我可以编辑这个问题,但是我不太愿意改变你想说的话.我发表了评论,因为我觉得这对你来说很重要,对于那些要求的人和后来的任何人都知道,因为答案是错误的.我现在正在删除我的downvote. (3认同)