c++crtptemplate-instantiation

Force template instantiation via typedef template<typename T, T> - why it works?


I am learning forcing template instantiantion.
It works, but I am still curious :-

#include <iostream>
#include <string>
template <typename T, T>struct NonTypeParameter { };//#1#
int lala=0;
template <typename T> class InitCRTP{
    public: static int init;
    public: using dummy=NonTypeParameter<int&, init>;   //#2#
};
template <typename T> int InitCRTP<T>::init = lala++;
class WantInit : public InitCRTP<WantInit>{
};
int main(){
    std::cout << lala << std::endl;
}

It prints 1, because InitCRTP<WantInit>::init is instantiated correctly.

Observation

  1. If I remove the line #2#, it will print 0. (InitCRTP<WantInit>::init is not instantiated).
  2. If I change #2# from int& to int, I will get :-

    error: the value of 'InitCRTP::init' is not usable in a constant expression

  3. If I change #1# to template <T>struct NonTypeParameter { }; and #2# to public: using dummy=NonTypeParameter<init>; I will get :-

    error: 'T' has not been declared

Question

  1. Why the line #2# is enough to force instantiation?
    In my opinion, it is just a typedef within template class that is not accessed by anyone.

  2. Why do I need int& as another template parameter to make it compilable?
    A probably more correct question : What is the name of the technique?

Original post : Force explicit template instantiation with CRTP


Solution

  • Why the line #2# is enough to force instantiation?

    To supply the second argument, the compiler has to bind a reference. Meaning it ODR-uses the static variable, so the variable has to exist and have a unique identity. Ergo, its definition is instantiated.

    When you use plain int, the second parameter can only accept integer constant expressions. A non-const static is not usable in a constant expression.

    Why do I need int& as another template parameter to make it compilable?

    You need to declare the type of the reference for the second parameter to have a type the compiler can check against. Well, prior to C++17 you needed to anyway. Nowadays we can use a placeholder type instead.

    template <auto&>struct NonTypeParameter { };//#1#
    using dummy=NonTypeParameter<init>;//#2#
    

    That will ODR-use the passed in static without having to specify a reference type explicitly.