Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-type template argument is not a constant expression

I have the following code:

#include <cstdlib>
#include <cstdio>
#include <atomic>

enum ATYPE { Undefined = 0, typeA, typeB, typeC };

template<ATYPE TYPE = Undefined>
struct Object
{
    Object() { counter++; }
    static std::atomic<int> counter;
};

template<ATYPE TYPE>
std::atomic<int> Object<TYPE>::counter(1);

template<ATYPE TYPE>
void test()
{
    printf("in test\n");
    Object<TYPE> o;
}

int main(int argc, char **argv)
{
    test<typeA>();
    printf("%d\n", Object<typeA>::counter.load());
    Object<typeA>::counter.store(0);
    for (int i = 0; i < sizeof(ATYPE); ++i) {
        Object<static_cast<ATYPE>(i)>::counter.store(0);
    }
    return 0;
}

When I compile with the following command line:

clang++ -o test -std=c++11 -stdlib=libc++ test.cpp

I get the following errors:

test.cpp:32:20: error: non-type template argument is not a constant expression
Object<static_cast<ATYPE>(i)>::counter.store(0);
^~~~~~~~~~~~~~~~~~~~~
test.cpp:32:39: note: read of non-const variable 'i' is not allowed in a constant expression
Object<static_cast<ATYPE>(i)>::counter.store(0);
^
testray.cpp:31:18: note: declared here
for (int i = 0; i < sizeof(ATYPE); ++i) {

I understand the problem I believe. The argument of the template needs to be a constexpr and i clearly is not. So the question is, are they possible changes I can do to get this working. By this working, I mean, can I somehow have a better way of resetting these static counters from this template class for each type in ATYPE, other than just doing it manually:

Object<Undefined>::counter.store(0);
Object<typeA>::counter.store(0);
...

Which is not so elegant and practical when ATYPE contains many types.

Thanks a lot for your help and advices.

like image 496
user18490 Avatar asked Aug 11 '13 23:08

user18490


1 Answers

For these kind of things, recursion is typically a simple solution:

#include <type_traits>

enum ATYPE { Undefined = 0, typeA, typeB, typeC, ATYPE_END };

void reset_Object_counter(std::integral_constant<ATYPE, ATYPE_END>)
{}

template < ATYPE n = Undefined >
void reset_Object_counter(std::integral_constant<ATYPE, n> p = {})
{
    Object<p>::counter.store(0);
    reset_Object_counter(std::integral_constant<ATYPE,
                                                static_cast<ATYPE>(n+1)>{});
}

For this case, AFAIK, function template specialization works as well (instead of the first overload):

template<>
void reset_Object_counter<ENUM_END>(std::integral_constant<ATYPE, ENUM_END>)
{}

Either way, the usage is just reset_Object_counter(); to set all Object<...>'s counters to 0.


The integral_constant solution is actually a bit overkill here, for this problem a non-type template parameter is sufficient (because the function template specialization can take the place of the overload ending the recursion).

template < ATYPE n = Undefined >
void reset_Object_counter()
{
    Object<n>::counter.store(0);
    reset_Object_counter<static_cast<ATYPE>(n+1)>();
}
template<>
void reset_Object_counter<ENUM_END>()
{}
like image 85
dyp Avatar answered Nov 08 '22 00:11

dyp