25

The following code compiles on a wide range of gcc and clang versions - when compiled and run with gcc 5.3.1, it prints

A()

then aborts with a pure virtual call error.

#include <stdio.h>

class A
{
public:
    A() {
        printf("A()\n");
    }
    virtual void b() const = 0;
};

int main()
{
    const A& a{};
    a.b();
    return 0;
}

I realise binding a reference to a temporary is not ideal (though I think this case is covered by some sort of lifetime extension) - but it also works when trying to call a method that takes a const reference like:

Foo({});

For convenience here's an example of it compiling with clang 3.2: Compiler Explorer

Destructor
  • 14,123
  • 11
  • 61
  • 126
Matt
  • 253
  • 2
  • 5
  • 1
    Constructing an abstract class through pointer or reference is allowed, remember? – DeiDei May 04 '16 at 01:50
  • 2
    @DeiDei I don't remember that, but it would explain why. EDIT: To be clear, it is actually constructing an object, not just a reference. – Matt May 04 '16 at 01:52
  • "sort of lifetime extension" -- the life ends at the statment completions - so at the ';' -- but since nothing else is happening you are just continuing with undefined behavior – Soren May 04 '16 at 01:53
  • 1
    @Soren I realise that typically that is the case (And that's why I mentioned lifetime extension at all). This is a simplified case for the example - really the "problem" is that it calls the constructor in the first place, the call is just to illustrate that it is the correct type etc. – Matt May 04 '16 at 01:55
  • 4
    @Soren When you bind a temporary to a const reference, the lifetime of the temporary is extended to the lifetime of the reference. – user253751 May 04 '16 at 01:56
  • 1
    @immibis -- then I learned something new today :-) – Soren May 04 '16 at 01:58
  • 2
    @DeiDei I don't remember that either, and the reference pages I'm finding on the Web seem to be saying the opposite. Can you hint how one might find the documentation on this? – David K May 04 '16 at 02:54
  • @DavidK http://en.cppreference.com/w/cpp/language/abstract_class – DeiDei May 04 '16 at 02:59
  • 1
    @DeiDei That's the first page I checked! It has an example `Abstract& a = b;`, but `b` was a previously constructed object of a concrete class. So we can make a new reference of type `Abstract&`, but I wouldn't have called that constructing an object. I feel like I'm missing something here; probably just misunderstood what you wrote. :-) – David K May 04 '16 at 03:04
  • 2
    @DeiDei: In order to bind a temporary to a reference, you must first construct a temporary. And that requires constructing an object of that type. And you're not allowed to construct complete objects of abstract class types. So where's the part that says you're allowed to construct a temporary of type `A`? – Nicol Bolas May 04 '16 at 03:23
  • @NicolBolas: I've filled a bug on gcc at: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70939 if you haven't done !!! – Destructor May 04 '16 at 06:41
  • @NicolBolas: I've filled a bug report to clang++ at: https://llvm.org/bugs/show_bug.cgi?id=27637 if you haven't done !!! – Destructor May 04 '16 at 06:46

1 Answers1

17

Why do gcc and clang allow me to construct an abstract class?

Because they're broken, according to the standard.

Section 10.4 defines how abstract classes work. It contains this line (in C++14):

no objects of an abstract class can be created except as subobjects of a class derived from it.

The initialization rules for references with braced-init-lists will construct a temporary and bind it to the reference. Temporaries are objects. As such, the code you wrote above will attempt to create an "object of an abstract class" as something other than a "subobject of a class derived from it."

Something the standard expressly forbids. There is no ambiguity in the standard in this regard. While 10.4, p3 does specify places that the compiler is required to out-right error if you type them (declaring abstract classes as function parameters, explicit conversions, etc), the standard still requires implementations to forbid the construction of an abstract class as something other than a "subobject of a class derived from it."

A temporary is not a "subobject of a class derived from it." And therefore, compilers are obligated to forbid this.

Any compiler which does not has a bug.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    Looks right to me, that section doesn't seem to leave any way around it. I've now checked MSVC 2015 which also simply prints an error: "'A': cannot instantiate abstract class", so at least it's not _all_ compilers – Matt May 04 '16 at 03:58
  • You forgot to mention in your answer that OP's code invokes undefined behaviour IMHO. See: http://melpon.org/wandbox/permlink/b8w0T43wNxvzeTME . – Destructor May 04 '16 at 06:16
  • @Destructor I did mention the pure virtual call in the OP, and pure virtual calls are definitely undefined behaviour. But according to the spec I would argue the code doesn't invoke UB because it isn't allowed to compile. Thanks for submitting the bug reports though! – Matt May 04 '16 at 07:25
  • @Matt: yes definitely it isn't allowed to compiler according to standard. – Destructor May 04 '16 at 07:51
  • @Matt: clang++ has fixed this bug. See live demo [here](https://wandbox.org/permlink/e575tvq2T8hjSYi8) . – Destructor Apr 10 '17 at 06:03