C++ template function, counting all template class specializations exactly before its point of instantiation

Viewed 250

Suppose, we have

template <int> struct Node;

It is required to get "reusable" compile-time function, which counts all Node specializations, defined exactly before every call of that function (from different points of translation unit). That is, number of specializations before point of function template instantiation. To simplify solution suppose that Node specializations are introduced sequentionally, starting from zero: 0,1,2,3...

Ok, here is C++ code:

#include <type_traits>

template <typename T>  decltype(T{}) InstanceOf(int);
template <typename T>           void InstanceOf(...);

template <typename K, template <int> typename T, int N = 0, 
          typename = decltype(InstanceOf<T<N>>(0))>
struct NCount
     : NCount<K, T, N + 1> {};

template <typename K, template <int> typename T, int N>
struct NCount<K, T, N, void>
     : std::integral_constant<int, N> {};

template <int> struct Node;

template <typename K = void, template <int> typename T = Node>
constexpr auto Count()
{
    return NCount<K, T>::value;
}    

template <> struct Node<0> {};
template <> struct Node<1> {};

int main()
{
    return Count(); //expected: 2
}

template <> struct Node<2> {};

Almost any code modification results in issue for some/all compilers (gcc/clang 9+ is interesting first of all):

1. if Count() body is not fully-dependent from template typename list, gcc fails:

    template <typename K = void>
    constexpr auto Count()
    {
        return NCount<K, Node>::value; //main: 0 returned
    }    

"Workaround" is to move the first specialization (Node<0>) before Count() template definition:

    template <> struct Node<0> {};

    template <typename K = void>
    constexpr auto Count()
    {
        return NCount<K, Node>::value; //main: 2 returned
    }

FYI: if K is removed from inside body in this case, we get another result (like body pre-compiled "in the place" with knowledge about single Node specialization):

    template <> struct Node<0> {};

    template <typename K = void>
    constexpr auto Count()
    {
        return NCount<void, Node>::value; //main: 1 returned
    }

2. if Count() return value type is specified (it is known - int), then totally all specializations is counted by both compilers:

    template <typename K = void, template <int> typename T = Node>
    constexpr int Count()
    {
        return NCount<K, T>::value; //main: 3 returned
    }  

Seems, that understanding function signature, compiler just "puts the call" of Count() inside main, but body of the function template will be compiled "later", after all cpp-file content is parsed (plus Node<2> spec). So the "auto" is the way to make compiler calculate NCount exactly in POI to get desired number.

Note that K-type purpose is to provide ability to be called as many times as required from any point of code. K-type must be unique every time (with respect to all other Count<K>() calls) to get actual value.

The questions are:

  • is there any compiler bug (including "modification" comments);
  • is this solution ill-formed (where);
  • if yes, could you give c++ standard compliant reliable solution?

p.s. I know about standard paragraphs, which tell us that class template specialization could not be used in some point if it is not defined before. But. Note that Count() first of all gets knowledge about available specializations before doing smth (indeed, in this example - no processing). Moreover, Node class main template just can not be used if not specialized - it has no definition! So...

p.s.s. In fact, all gcc/clang 5+ and icc give desirable 2. MSVC gives 0. Other [more specific] compilers give 0 also. Checked via godbolt.org, wandbox.org.

Thanks!

0 Answers
Related