简体   繁体   中英

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. 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() . 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().

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. Just because you don't call a function doesn't mean you can't . You could still call base<std::string>::get() directly.

You instantiated base<std::string> and used it as a base class. 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. 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.

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.[...]

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.

T t = -1.0;

Of course, this wouldn't compile if T is std::string . It has nothing to do with get being virtual and which function will be called at runtime. 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.

The problem is in this method (where 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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