Oleg Bogdanov Oleg Bogdanov - 1 month ago 6
C++ Question

clang vs gcc in abstract class handling in compile time

One of these issues of nlohmann/json opensource library drew my attention.

I have a minimal reproduction of the case that does not compile under few version of desktop gcc (4.8, 4.9, also tried 5+) but compiles well with mac clang and Android ndk's gcc 4.9

#include <limits>

struct base {
virtual void foo() = 0;
};

int main() {
(void)numeric_limits<base>::is_signed;
}


GCC is trying to instantiate
std::numeric_limits
with base class instead of derived:

/usr/include/c++/4.8/limits: In instantiation of 'struct std::numeric_limits<base>': main.cpp:11:94: required from here
/usr/include/c++/4.8/limits:309:7: error: cannot allocate an object of abstract type 'base'
min() _GLIBCXX_USE_NOEXCEPT { return _Tp(); }


I'm not quite sure if this is a known compiler bug (in case of failure) or feature/relaxed rule (in case of success)

I've tried to work it around with
std::is_abstract
but it does not help, looks like 'short-circuit' evaluation is not happening in enable_if and error stays the same

My question mainly is not how to fix this for gcc but weather this is compiler bug or code bug




Edit: added "more minimal" example without any standard library dependency:

template <typename T>
struct foo
{
static T bar();
static constexpr bool value = true;
};

struct abstract
{
virtual ~abstract() = 0;
};

int main()
{
(void) foo<abstract>::value;
}


Compiles on clang 3.9.0, but gcc 7 snapshot emits an error for the invalid return type of
foo<abstract>::bar
.




Edit2: I'm a bit surprised that my initial question was edited without my consent, was not aware that SO allows that :) Thanks for help though I think it brought a bit of confusion and wrong answers, mainly because text and code were not connected anymore

Answer

No, it is not a bug. It is just a bad test.

Description

The only difference between GCC and clang in this case is the way they process template class functions:

  • GCC: All of them in once.
  • clang: Only the one that has been used.

In our first example, the function min() doesn't get called, and therefore, clang doesn't has a problem with it. GCC, parses all the functions and find out that min() is invalid.

In the second example, the same happens: bar() doesn't get called by anyone and therefore clang is OK about it being ill-formed. But again, GCC, have a problem with it, although hasn't been used anywhere in the program.

A good test

Saying that something is bad is not enough, let's fix it: this example would fail with both GCC and clang with almost the same error (invalid abstract return type ‘base’ or allocating an object of abstract class type 'base').

#include <limits>

struct base {
    virtual void foo() = 0;
};

int main() {
    (void)std::numeric_limits<base>::min();
}