简体   繁体   English

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

[英]Calling a virtual member function from inside the (template) base class

Suppose I have the following: 假设我有以下内容:

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

It seems to me that d.print() should call derived::get() because get() is virtual. 在我看来, d.print()应该调用derived::get()因为get()是虚拟的。 However, I'm getting a compiler error saying that I can't initialize a string to -1.0 , which means that the compiler is trying to call base::get() when I call d.print() . 但是,我收到编译器错误,说我无法将string初始化为-1.0 ,这意味着当我调用d.print()时,编译器正在尝试调用base::get() d.print() What's going on? 这是怎么回事?

However, I'm getting a compiler error saying that I can't initialize a string to -1.0, which means that the compiler is trying to call base::get() when I call d.print(). 但是,我收到编译器错误,说我无法将字符串初始化为-1.0,这意味着当我调用d.print()时,编译器正在尝试调用base :: get()。

No, that compiler error means that the compiler is trying to instantiate base<std::string>::get() , which it must do because derived uses base<std::string> as a base class. 不,编译器错误意味着编译器正在尝试实例化base<std::string>::get() ,它必须这样做,因为derived使用base<std::string>作为基类。 Just because you don't call a function doesn't mean you can't . 仅仅因为你没有调用函数并不意味着你不能 You could still call base<std::string>::get() directly. 你仍然可以直接调用base<std::string>::get()

You instantiated base<std::string> and used it as a base class. 您实例化了base<std::string>并将其用作基类。 Since base<std::string>::get() is a virtual function, it is considered "used" by the fact that you use base<std::string> as a base class. 由于base<std::string>::get()是一个虚函数,因此使用base<std::string>作为基类这一事实将其视为“使用”。 Since it is in use, it must be instantiated. 由于它正在使用中,因此必须进行实例化。 So the compiler must and will attempt to compile the function. 因此编译器必须并将尝试编译该函数。

And since std::string cannot be implicitly constructed from a float, the compiler errors out from a failed template substitution. 由于std::string不能从float中隐式构造,因此编译器会因失败的模板替换而出错。

When you implicitly instantiate a class template, only those member functions that are used will be instantiated, as you already know or else you would not be asking this. 当您隐式实例化一个类模板,仅使用这些成员函数将被实例化,因为你已经知道了,否则你就不会问这个。 The problem is that the definition of use in the standard may not be exactly what you expect, and in particular, any virtual function is used if it is not pure 问题是标准中的使用定义可能不完全符合您的预期,特别是,如果它不是纯粹的话,任何函数都会被使用

§3.2p2 [...]A virtual member function is odr-used if it is not pure.[...] §3.2p2[...]如果虚拟成员函数不纯,则使用它是有用的。[...]

And this means that base::get is used even if your code does not explicitly call it, and thus the compiler will implicitly instantiate it and trigger the compiler error that you are seeing. 这意味着即使您的代码没有显式调用它也会使用base::get ,因此编译器将隐式实例化它并触发您看到的编译器错误。

T t = -1.0;

Of course, this wouldn't compile if T is std::string . 当然,如果Tstd::string ,这将无法编译。 It has nothing to do with get being virtual and which function will be called at runtime. 它与get virtual无关,在运行时将调用哪个函数。 Runtime comes after code gets compiled to machine code, and your code wouldn't even compile. 运行时在代码编译为机器代码后出现,您的代码甚至无法编译。

Why don't you do this: 你为什么不这样做:

T t = T(); 

Again it requires T to have a default constructor. 同样,它需要T具有默认构造函数。

The problem is in this method (where T=std::string) : 问题出在这个方法中(其中T = std :: string):

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

The compiler is right. 编译器是对的。 You can not initialize std::string using a double value. 您不能使用double值初始化std :: string。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM