Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to understand templates and name lookup

I am trying to understand the following code snippets

Snippet #1

template <typename T>
struct A
{
    static constexpr int VB = T::VD;
};

struct B : A<B>
{
};

Neither gcc9 nor clang9 throw an error here.

Q. Why does this code compile? Aren't we instantiating A<B> when inheriting from B? There is no VD in B, so shouldn't the compiler throw an error here?

Snippet #2

template <typename T>
struct A
{
    static constexpr auto AB = T::AD; // <- No member named AD in B
};

struct B : A<B>
{
    static constexpr auto AD = 0xD;
};

In this case, gcc9 compiles fine but clang9 throws an error saying "No member named AD in B".

Q. Why does it compile with gcc9/why doesn't it compile with clang9?

Snippet #3

template <typename T>
struct A
{
    using TB = typename T::TD;
};

struct B : A<B>
{
    using TD = int;
};

Here both clang9 and gcc9 throw an error. gcc9 says "invalid use of incomplete type 'struct B'".

Q. If struct B is incomplete here then why is it not incomplete in snippet #2?

Compiler flags used: -std=c++17 -O3 -Wall -Werror. Thanks in Advance!!!

like image 891
Empty Space Avatar asked Dec 16 '19 09:12

Empty Space


1 Answers

I believe these essentially boil down to [temp.inst]/2 (emphasis mine):

The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or noexcept-specifiers of the class member functions, member classes, scoped member enumerations, static data members, member templates, and friends; […]

and [temp.inst]/9

An implementation shall not implicitly instantiate […] a static data member of a class template […] unless such instantiation is required.

The wording in the standard concerning implicit template instantiation leaves many details open to interpretation. In general, it would seem to me that you simply cannot rely on parts of a template not being instantiated unless the specification explicitly says so. Thus:

Snippet #1

Q. Why does this code compile? Aren't we instantiating A when inheriting from B? There is no VD in B, so shouldn't the compiler throw an error here?

You are instantiating A<B>. But instantiating A<B> only instantiates the declarations, not the definitions of its static data members. VB is never used in a way that would require a definition to exist. The compiler should accept this code.

Snippet #2

Q. Why does it compile with gcc9/why doesn't it compile with clang9?

As pointed out by Jarod42, the declaration of AB contains a placeholder type. It would seem to me that the wording of the standard is not really clear on what is supposed to happen here. Does the instantiation of the declaration of a static data member that contains a placeholder type trigger placeholder type deduction and, thus, constitute a use that requires the definition of the static data member? I can't find wording in the standard that would clearly say either yes or no to that. Thus, I would say that both interpretations are equally valid here and, thus, GCC and clang are both right…

Snippet #3

Q. If struct B is incomplete here then why is it not incomplete in snippet #2?

A class type is only complete at the point where you reach the closing } of the class-specifier [class.mem]/6. Thus, B is incomplete during the implicit instantiation of A<B> in all your snippets. It's just that this was irrelevant for Snippet #1. In Snippet #2, clang did give you an error No member named AD in B as a result. Similar to the case of Snippet #2, I can't find wording on when exactly member alias declarations would be instantiated. However, unlike for the definition of static data members, there is no wording in place to explicitly prevent the instantiation of member alias declarations during implicit instantiation of a class template. Thus, I would say that the behavior of both GCC and clang is a valid interpretation of the standard in this case…

like image 62
Michael Kenzel Avatar answered Nov 09 '22 00:11

Michael Kenzel