非类型模板参数不是常量表达式。

5

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;
}

当我使用以下命令行进行编译时:

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

我收到了以下的错误信息:
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) {

我理解这个问题。模板的参数需要是constexpr,但i显然不是。所以问题是,有没有可能做出一些改变来使它工作。通过“工作”,我的意思是,我是否可以以某种更好的方式为ATYPE中的每种类型重置这些静态计数器,而不仅仅是手动执行它:
Object<Undefined>::counter.store(0);
Object<typeA>::counter.store(0);
...

当ATYPE包含许多类型时,这并不太优雅和实用。非常感谢您的帮助和建议。

1
你的枚举循环方式本来就是错误的。正如错误所示,i 也不是一个常量表达式。 - Rapptz
@Rapptz,您能否对您的第一条评论(枚举循环错误)进行更具体的说明。我知道i不是const,所以我想知道是否有办法使这个工作。是否有一种方法可以遍历枚举的所有元素并重置计数器?谢谢。 - user18490
Rapptz 的意思是 i 是动态的,不能用作编译时模板参数。 - a.lasram
@user18490 提示:sizeof(enum) == sizeof(std::underlying_type<enum>::type) - Rapptz
@greatwolf,是的,谢谢你,我终于明白Rapptz想说什么了。我仍然有兴趣找到一种循环遍历所有可能的Object<>::counter并重置它们的方法,而不是手动为枚举中的每个常量执行此操作。有人知道吗? - user18490
显示剩余3条评论
1个回答

7

对于这类问题,递归通常是一个简单的解决方案:

#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)>{});
}

对于这种情况,据我所知,函数模板特化同样有效(而不是第一个重载):
template<>
void reset_Object_counter<ENUM_END>(std::integral_constant<ATYPE, ENUM_END>)
{}

无论哪种方式,使用方法都是 reset_Object_counter();,将所有 Object<...> 的计数器设置为 0


integral_constant 的解决方案实际上有点过度设计,在这个问题中,非类型模板参数就足够了(因为函数模板特化可以取代递归结束的重载)。

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>()
{}

太棒了。这让我意识到我对C++还不够熟悉。我从未听说过integral_constant……但我正在赶上C++11中所有新事物,似乎需要一辈子的时间。非常感谢你的回答,也感谢其他所有人。 - user18490
@user18490 这里甚至不需要 integral_constant ;) - dyp
是的,我实际上正在尝试编写代码而不使用它,因为我猜想它可以在没有它的情况下工作,但无论如何,我很高兴学到了一些东西。感谢您的这个补充说明。 - user18490
感谢您添加第二个解决方案。我已经完成了一半,但是有了您的示例,我节省了时间。再次感谢,非常非常有帮助。 - user18490

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接