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

1

26

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

Peachy answered 4/5, 2016 at 1:46 Comment(12)
Constructing an abstract class through pointer or reference is allowed, remember?Neoplasty
@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.Peachy
"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 behaviorMeyerbeer
@Meyerbeer 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.Peachy
@Meyerbeer When you bind a temporary to a const reference, the lifetime of the temporary is extended to the lifetime of the reference.Lovato
@immibis -- then I learned something new today :-)Meyerbeer
@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?Goosestep
@DavidK en.cppreference.com/w/cpp/language/abstract_classNeoplasty
@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. :-)Goosestep
@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?Renowned
@NicolBolas: I've filled a bug on gcc at: gcc.gnu.org/bugzilla/show_bug.cgi?id=70939 if you haven't done !!!Headland
@NicolBolas: I've filled a bug report to clang++ at: llvm.org/bugs/show_bug.cgi?id=27637 if you haven't done !!!Headland
R
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.

Renowned answered 4/5, 2016 at 3:47 Comment(5)
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 compilersPeachy
You forgot to mention in your answer that OP's code invokes undefined behaviour IMHO. See: melpon.org/wandbox/permlink/b8w0T43wNxvzeTME .Headland
@Headland 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!Peachy
@Matt: yes definitely it isn't allowed to compiler according to standard.Headland
@Matt: clang++ has fixed this bug. See live demo here .Headland

© 2022 - 2024 — McMap. All rights reserved.