2

I don't know exactly why the following code compiles and works (it works well).

#include <iostream>

struct Base
{
    virtual std::ostream& display(std::ostream& os) const = 0;
    friend std::ostream& operator<<(std::ostream& lhs, const Base& rhs)
    {
        return rhs.display(lhs);
    }
};

struct A: Base
{
    virtual std::ostream& display(std::ostream& os) const
    {
        return os << "A" << std::endl;
    }
};

struct B: A
{
    virtual std::ostream& display(std::ostream& os) const
    {
        return os << "B" << std::endl;
    }
};

int main()
{
    A a;
    std::cout << a << std::endl;

    B b;
    std::cout << b << std::endl;
}

I am defining the operator<< only once inside the Base class, which calls the pure virtual display function. This scheme is usually used to avoid re-writing operator<< in the derived classes, i.e. define it only once in the base class then use virtual dispatch with another function (in my case, display()).

See Live on Coliru

Can you explain why I am able to call a pure virtual function inside the Base class in the implementation of friend std::ostream& operator<<(...)? I thought this shouldn't be possible.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • 1
    Why wouldn't it be possible? – user253751 Jan 10 '15 at 02:44
  • If you couldn't call pure virtual functions through a pointer or reference to the base class they wouldn't be very useful. – Captain Obvlious Jan 10 '15 at 02:45
  • Maybe I'm missing something but I though one cannot "call" a pure virtual function, only overload it and call it in a derived class. – vsoftco Jan 10 '15 at 02:45
  • 1
    Sure you can call it, you just can't instantiate a class that has a pure virtual function. – Captain Obvlious Jan 10 '15 at 02:46
  • @CaptainObvlious I think I asked a stupid question :-/ Of course, you call it through the reference.... I'll take a weekend break :) – vsoftco Jan 10 '15 at 02:46
  • On that note, you can even provide a definition of a pure virtual function _and_ call it. – Captain Obvlious Jan 10 '15 at 02:46
  • I assume the code you are showing isn't yours. It is a common idiom to define an operator that simply forwards to a `virtual` function. This is done to implement “polymorphic operators” because operators themselves cannot be `virtual`. (One can also view this as a special case of the *non-virtual interface*.) Does this make sense? – 5gon12eder Jan 10 '15 at 02:49
  • @5gon12eder yes that's the idea, the code is mine though (I mean, was just playing with this snippet after responding to a different SO question http://stackoverflow.com/a/27871137/3093378 ). I realized now that everything is OK – vsoftco Jan 10 '15 at 02:51
  • @CaptainObvlious can you give a simple example when you can define AND call the pure vfunc? I remember seeing some monstrosity like this ;) but cannot remember where and how it works. – vsoftco Jan 10 '15 at 02:56
  • @vsoftco Make the function pure in class with `= 0;`, define it out of class, and call it using a non-virtual call (with a *qualified-id*). – T.C. Jan 10 '15 at 02:59
  • You define it line you would any other member function and you invoke it explicitly (as T.C. noted). – Captain Obvlious Jan 10 '15 at 03:00
  • @T.C thanks, got it, although I gotta say it's extremely weird you are allowed to do this. @Captain Obvlious, I am not getting the syntax right, how do you write it inline? `virtual return_type f(params){...} = 0`? This doesn't seem to work – vsoftco Jan 10 '15 at 03:02
  • @vsoftco You can't do it inline. – T.C. Jan 10 '15 at 03:05
  • @T.C., ok, one more thing learned for today :) Thanks to all the comments/answers! – vsoftco Jan 10 '15 at 03:07

1 Answers1

3

You are able to call the pure virtual function because by the time you call it the function is no longer pure virtual: a derived class must override it in order to stop being "abstract".

The compiler knows that you cannot instantiate class Base by itself. This means that you wouldn't be able to call operator << on any class that does not provide a suitable override for your pure virtual function. That is why the compiler lets you make a call: it knows that there is going to be an implementation at runtime.

Note: the only way to make a call to a pure virtual function is to call it from a constructor of the base class. Since the function is pure virtual, this causes undefined behavior; modern compilers warn you about this problem.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • yeah, I managed to confuse myself, I am calling it via a reference, so the usual rules of virtual dispatch apply... I am ashamed of asking this question... – vsoftco Jan 10 '15 at 02:49