Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 791
eba Avatar asked Dec 16 '22 07:12

eba


2 Answers

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.

like image 199
Nicol Bolas Avatar answered Dec 31 '22 00:12

Nicol Bolas


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.

like image 25
David Rodríguez - dribeas Avatar answered Dec 31 '22 01:12

David Rodríguez - dribeas