gpt4 book ai didi

c++ - 模板类中的编译时计数器

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:19:24 25 4
gpt4 key购买 nike

我有一个我使用多年的编译时计数器,灵感来自 these answers .它在 C++03/11 中运行,据我测试,在主要编译器上运行得相对较好:

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_)>)
}

下面的测试compiles and runs perfectly (预期输出为 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> struct Testtest<Test<42> >() in main ),clang and gcc both fail to compile ,提示我正在重新定义计数器函数(而 msvc 编译它没有问题)。由于某种原因,编译器无法计算模板类中的 sizeof。

clang 在第三个 INC_COUNT 找到错误, 而 gcc 在第二个找到它。

我手动展开了这个宏,并且:

  • 对于 clang,它给出

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

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

  • 对于 gcc:移动 +2sizeof 之前是唯一的解决方法

令人遗憾的是,当包含在宏中时,这些变通办法似乎不起作用。这就像编译器在一段时间后忘记了如何计算 sizeof 的结果......

为什么会这样?我做错了什么,还是只是编译器错误(因为 clang 和 gcc 甚至不报告同一行)?

注意:我知道there is a gcc bug about this counter .问题不在于这个错误。

最佳答案

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

A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.

您使用重载决策来选择 _counteruu 的适当重载.但是,在例如的初始化程序中a ,选择了一个重载(=声明),如果我们要在 Test 结束时执行重载决议,则不会选择该重载,例如在 d 的初始化程序中.因此 _counteruuTest 的完整范围内重新评估时,引用另一个不同的声明.

要准确显示我指的是哪些调用,请考虑 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 的初始化程序中的调用将选择第一个过载;但是重新评估这个初始化器会选择最后一个。


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

关于c++ - 模板类中的编译时计数器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31720516/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com