Question

Code example:

template <int x>
struct SUM
{
    static_assert(x >= 0, "X must be greater or equal to 0");
    enum {VALUE = x + SUM<x-1>::VALUE};
};

template<>
struct SUM<0>
{
    enum {VALUE = 0};
};

int main()
{
    std::cout << SUM<-1>::VALUE << std::endl;
    return 0;
}

Why compiler does not break compilation on first static_assert but continue work till reaching maximum instantiation depth?

Invoking: GCC C++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -std=c++11 -MMD -MP -MF"src/Main.d" -MT"src/Main.d" -o "src/Main.o" "../src/Main.cpp"
../src/Main.cpp: In instantiation of ‘struct SUM<-1>’:
../src/Main.cpp:47:22:   required from here
../src/Main.cpp:26:2: error: static assertion failed: X must be greater or equal to 0
  static_assert(x >= 0, "X must be greater or equal to 0");
  ^

......

../src/Main.cpp: In instantiation of ‘struct SUM<-2>’:
../src/Main.cpp: In instantiation of ‘struct SUM<-900>’:
../src/Main.cpp:27:18:   recursively required from ‘struct SUM<-2>’
../src/Main.cpp:27:18:   required from ‘struct SUM<-1>’
../src/Main.cpp:47:22:   required from here
../src/Main.cpp:26:2: error: static assertion failed: X must be greater or equal to 0
../src/Main.cpp:27:18: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) instantiating ‘struct SUM<-901>’
  enum {VALUE = x + SUM<x-1>::VALUE};
                  ^
../src/Main.cpp:27:18:   recursively required from ‘struct SUM<-2>’
../src/Main.cpp:27:18:   required from ‘struct SUM<-1>’
../src/Main.cpp:47:22:   required from here

../src/Main.cpp:27:18: error: incomplete type ‘SUM<-901>’ used in nested name specifier
make: *** [src/Main.o] Error 1

13:04:05 Build Finished (took 6s.877ms)

The only problem here is that it takes a lot of time till break, and produces a lot of output. Is there a way to make this better? Used compiler: gcc version 4.8.1

Was it helpful?

Solution

There is nothing in the language that requires compilation to abort immediately, so anything you get that directly answers your question will necessarily be implementation-specific and won't avoid the instantiations on other implementations. What I think would be better is to rework your code in such a way that it doesn't have any way to continue with the instantiations. One possible way to do this is with std::enable_if:

#include <iostream>
#include <type_traits>

template <int x, typename = typename std::enable_if<x >= 0>::type>
struct SUM_impl
{
    enum {VALUE = x + SUM_impl<x-1>::VALUE};
};

template<>
struct SUM_impl<0>
{
    enum {VALUE = 0};
};

template <int x>
struct SUM
{
    static_assert(x >= 0, "X must be greater or equal to 0");
    enum {VALUE = SUM_impl<x>::VALUE};
};

int main()
{
    std::cout << SUM<-1>::VALUE << std::endl;
    return 0;
}

This way, the static_assert in SUM prints out a user-friendly message. The enable_if in SUM_impl forcibly rejects anything where x < 0, before the template would be instantiated, and if the template doesn't get instantiated, it cannot be instantiated recursively either.

I initially made SUM derive from SUM_impl, but not doing so (and copying its value) provides better diagnostics.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top