Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using a fundamental type as a base-class compile sometimes?

This compiles (tested with GCC 9 and Clang 9):

template<typename U>
struct inherit : U { };

int test(inherit<int> arg);

But this does not:

int test(inherit<int> arg) { }

Why does the first one compile?

like image 921
Nerp Avatar asked Mar 02 '23 19:03

Nerp


2 Answers

int test(inherit<int> arg); is just a declaration. As such, we don't need to know about inherit<int> yet. Because of that, the compiler will let it go.

With int test(inherit<int> arg) { } you now have a definition and now we need to know about inherit<int> so arg can be destroyed when the function exits. At that point the template is instantiated and you get an error because it is invalid.

Another reason to ignore the declaration is that inherit could later on get specialized for int and that specialization might actually be a valid class since you could have something like

template<>
struct inherit<int> { };

If you were to add that between int test(inherit<int> arg); and int test(inherit<int> arg) { } then the code would now compile since inherit<int> is now a valid type.

like image 82
NathanOliver Avatar answered Mar 05 '23 16:03

NathanOliver


I hope someone else will explain the why. I will use a phenomenological approach here ;).

Your first version also fails to compile once you actually instantiate a inherit<int>:

int main() {
    test( inherit<int>{} );
}

Error:

prog.cc: In instantiation of 'struct inherit<int>':
prog.cc:9:24:   required from here
prog.cc:4:8: error: base type 'int' fails to be a struct or class type
    4 | struct inherit : U { };
      |        ^~~~~~~

I could have simply tried to create a inherit<int> object (without calling test) to get similar error.

On the other hand, this is just a declaration: int test(inherit<int> arg); so before actually providing the definition there could be a specialization for inherit that enables us to also provide a valid definition for test:

template<typename U>
struct inherit : U { };

// looks broken
int test(inherit<int> arg);
// but that this point we dont really know yet what `inherit<int>` really is

// whoops inherit<int> is something different now   
template <> struct inherit<int> {};

// ... and now this is completely fine
int test(inherit<int> arg) {}

int main() {
    test( inherit<int>{} );
}
like image 42
463035818_is_not_a_number Avatar answered Mar 05 '23 16:03

463035818_is_not_a_number