3

I came up with the following piece of code thinking that it wouldn't work, but surprisingly to me it works absolutely fine, and I would like some explanation.

#include <cstdio>
#include <algorithm>

struct Abstract
{
    Abstract()
        { printf("\tConstructing Abstract instance.\n"); }

    virtual const char* name() const =0;
    auto talk() const -> decltype(*this)
    {
        printf("My name is %s.\n",name());
        return *this;
    }
};

struct Concrete
    : public Abstract
{
    Concrete() 
        { printf("\tConstructing Concrete instance.\n"); }
    const char* name() const
        { return "Bond"; }
};

int main()
{
    Concrete C;
    printf("James %s.\n",C.talk().name());
}

The output of this code is:

    Constructing Abstract instance.
    Constructing Concrete instance.
My name is Bond.
James Bond.

Q1 (This is non-specific to C++11, just remove decltype and replace auto by const Abstract&). Why does the compiler accept the syntax C.talk().name()?

Unless I am mistaken, I think the expression C.talk() could either be a prvalue (copy of Abstract, not the case because there is only one constructor output) or an lvalue reference, in which case I am not sure to what because an lvalue reference to an abstract class seems impossible to me too.

Q2 This leads me to the second question: how does decltype "know" to return some kind of reference, and not a plain value?

Jonathan H
  • 7,591
  • 5
  • 47
  • 80
  • 3
    An lvalue reference to an abstract class type is perfectly valid, just like a pointer to an abstract class type is valid. – dyp Aug 02 '14 at 17:22
  • *"how does `decltype` "know" to return some kind of reference, and not a plain value?"* That's the way `decltype` is defined. The expression `*this` is an lvalue-expression, hence `decltype` yields an lvalue reference. – dyp Aug 02 '14 at 17:23
  • I guess that answers my question then; does it behave like a dereferenced pointer to the abstract class? – Jonathan H Aug 02 '14 at 17:24
  • 1
    Yes, see e.g. http://stackoverflow.com/q/5019046 – dyp Aug 02 '14 at 17:25

1 Answers1

8

Q1: An lvalue reference to an abstract class is not only allowed, but actually essential for polymorphism to work. Note that the reference type corresponds to the static type of the expression, not the dynamic type of the object, which of course cannot be of the abstract class (well, actually that's also not completely true, but it's close enough for the purpose of this answer).

The syntax C.talk().name() is therefore accepted because the type Abstract contains a declaration of the member function name(), and it will be executed correctly because at execution time, the actually called function depends on the dynamic type, and that is Concrete and has an implementation of name().

Q2: *this is an lvalue expression, and decltype for an lvalue expression gives an lvalue reference.

celtschk
  • 19,311
  • 3
  • 39
  • 64