本文介绍了模板类中的编译时计数器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用多年的编译时间计数器,灵感来自这些答案.它可以在C ++ 03/11上运行,并且据我测试,在主要编译器上相对较好:

I have a compile-time counter that I used for years, inspired by these answers. It works in C++03/11, and as far as I tested, relatively well on major compilers:

namespace meta
{
    template<unsigned int n> struct Count { char data[n]; };
    template<int n> struct ICount : public ICount<n-1> {};
    template<> struct ICount<0> {};

    #define MAX_COUNT 64
    #define MAKE_COUNTER( _tag_ ) \
        static ::meta::Count<1> _counter ## _tag_ (::meta::ICount<1>)
    #define GET_COUNT( _tag_ ) \
        (sizeof(_counter ## _tag_ (::meta::ICount<MAX_COUNT + 1>())) - 1)
    #define INC_COUNT( _tag_ ) \
        static ::meta::Count<GET_COUNT(_tag_) + 2> _counter ## _tag_ (::meta::ICount<2 + GET_COUNT(_tag_)>)
}

以下测试编译并完美运行(预期输出为 01 2 3 ):

The following test compiles and runs perfectly (expected output is 0 1 2 3):

struct Test
{
    MAKE_COUNTER( uu );

    static const unsigned int a = GET_COUNT( uu );
    INC_COUNT( uu );
    static const unsigned int b = GET_COUNT( uu );
    INC_COUNT( uu );
    static const unsigned int c = GET_COUNT( uu );
    INC_COUNT( uu );
    static const unsigned int d = GET_COUNT( uu );

};

template<typename T>
void test()
{
    std::cout << T::a << " " << T::b << " " << T::c << " " << T::d << "\n";
}

int main()
{
    test<Test>();
}

但是,我发现一个案例是我看到clang和gcc发生了非常奇怪的行为.如果将 Test 更改为模板结构,则以int为例( template< int>结构测试 test< Test< 42>>()(在 main 中)), clang和gcc都无法编译,抱怨我正在重新定义计数器函数(而msvc编译它没有问题).由于某种原因,编译器无法在模板类中计算sizeof.

However, I found a case were I see a very strange behavior happening with clang and gcc. If you change Test to be a template struct, taking an int for example (template<int> struct Test, and test<Test<42> >() in main), clang and gcc both fail to compile, complaining that I am redefining the counter function (while msvc compiles it without problems). For some reason the compiler fails to compute a sizeof in a template class.

clang在第三个 INC_COUNT 处发现错误,而gcc在第二个 INC_COUNT 处发现错误.

clang find the error at the third INC_COUNT, while gcc find it at the second one.

我手动扩展了此宏,并且:

I manually expanded this macro, and:

  • 对于clang,它给出了

  • for clang, it gives

static ::meta::Count<GET_COUNT(uu)+2> _counteruu(::meta::ICount<(sizeof(_counteruu(::meta::ICount<65>())) - 1)+2>);
//                                                              ^                                            ^

删除带下划线的括号即可解决此问题.

removing the underlined parentheses solves the issue.

用于gcc:将 +2 移到 sizeof 之前是唯一的解决方法

for gcc: moving the +2 before the sizeof is the only work-around

可悲的是,这些解决方法在包含在宏中时似乎无效.就像编译器只是忘记了一段时间后如何计算sizeof的结果...

The sad note is that these workarounds seem not to work when included in the macros. It's like the compiler just forgets how to compute the result of sizeof after some time...

为什么会这样?我是在做错什么,还是仅仅是编译器的错误(因为clang和gcc甚至没有报告同一行)?

Why is this happening ? Am I doing something wrong, or is it just compiler bugs (since clang and gcc don't even report the same line) ?

注意:我知道有一个gcc有关此计数器的错误.问题与这个错误无关.

Note: I know there is a gcc bug about this counter. The question is not about this bug.

推荐答案

您的代码格式错误,无需诊断.§3.3.7/1,第二个要点:

Your code is ill-formed, no diagnostic required. §3.3.7/1, second bullet point:

您可以使用重载分辨率来选择 _counteruu 的适当重载.但是,在 a ,选择了一个重载(= declaration),如果我们要在 Test 的末尾执行重载解析,例如在 d .因此,在完整的 Test 范围内重新评估时, _counteruu 引用了另一个不同的声明.

You use overload resolution to select the appropriate overload of _counteruu. However, in the initializer of e.g. a, an overload (=declaration) is selected that wouldn't be selected if we were to perform overload resolution at the end of Test, such as in the initializer of d. Hence _counteruu refers to another, distinct declaration when re-evaluated in the completed scope of Test.

要显示我确切指的是哪些调用,请考虑对 Test 进行预处理的定义:

To show up which calls exactly I'm referring to, consider the preprocessed definition of Test:

struct Test
{
    // (1)
    static ::meta::Count<1> _counteruu (::meta::ICount<1>);
    static const unsigned int a = (sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1);
    // (2)
    static ::meta::Count<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2> _counteruu (::meta::ICount<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2>);
    static const unsigned int b = (sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1);
    // (3)
    static ::meta::Count<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2> _counteruu (::meta::ICount<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2>);
    static const unsigned int c = (sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1);
    // (4)
    static ::meta::Count<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2> _counteruu (::meta::ICount<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2>);
    static const unsigned int d = (sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1);

};

简化产量

struct Test
{
    // (1)
    static ::meta::Count<1> _counteruu (::meta::ICount<1>);
    static const unsigned int a = (sizeof(_counteruu (::meta::ICount<65>())) - 1);
    // (2)
    static ::meta::Count<2> _counteruu (::meta::ICount<2>);
    static const unsigned int b = (sizeof(_counteruu (::meta::ICount<65>())) - 1);
    // (3)
    static ::meta::Count<3> _counteruu (::meta::ICount<3>);
    static const unsigned int c = (sizeof(_counteruu (::meta::ICount<65>())) - 1);
    // (4)
    static ::meta::Count<4> _counteruu (::meta::ICount<4>);
    static const unsigned int d = (sizeof(_counteruu (::meta::ICount<65>())) - 1);
};

我们可以清楚地看到该机制现在是如何工作的:当 ICount< 足够大的数字 > 是由于对衍生基础转换的排名方式.但是,在 a 的初始值设定项中的调用将选择第一个重载.但是重新评估此初始值设定项将选择最后一个.

We can clearly see how the mechanism works now: Overload resolution will prefer the last added overload when ICount<some sufficiently large number> is passed due to the way derived-to-base conversions are ranked. However, the call in the initializer of a will select the first overload; But re-evaluating this initializer would select the last one.

这个要点也存在于C ++ 03中,但在§3.3.6中存在.

This bullet point existed in C++03 as well, but in §3.3.6.

这篇关于模板类中的编译时计数器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 10:58
查看更多