Syn*_*xis 5 c++ gcc templates clang
我有一个编译时计数器,我使用多年,受这些答案的启发.它适用于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_)>)
}
Run Code Online (Sandbox Code Playgroud)
以下测试编译并运行完美(预期输出为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>();
}
Run Code Online (Sandbox Code Playgroud)
然而,我发现一个案例,我发现clang和gcc发生了一种非常奇怪的行为.如果您更改Test为模板结构,以int为例(template<int> struct Test,并且test<Test<42> >()在main),clang和gcc都无法编译,抱怨我正在重新定义计数器函数(而msvc编译它没有问题).由于某种原因,编译器无法计算模板类中的sizeof.
clang在第三个找到错误INC_COUNT,而gcc在第二个找到它.
我手动扩展了这个宏,并且:
对于铿锵,它给出了
static ::meta::Count<GET_COUNT(uu)+2> _counteruu(::meta::ICount<(sizeof(_counteruu(::meta::ICount<65>())) - 1)+2>);
// ^ ^
Run Code Online (Sandbox Code Playgroud)
删除带下划线的括号可以解决问题.
对于gcc:移动+2之前sizeof是唯一的解决方法
遗憾的是,当包含在宏中时,这些变通办法似乎无法工作.这就像编译器忘记了一段时间后如何计算sizeof的结果......
为什么会这样?我做错了什么,或者只是编译器错误(因为clang和gcc甚至不报告相同的行)?
注意:我知道这个计数器有一个gcc错误.问题不在于这个错误.
您的代码格式错误,无需诊断.§3.3.7/ 1,第二点要点1:
N类中使用的名称S应在其上下文中引用相同的声明,并在完成的范围内重新评估S.违反此规则无需诊断.
您使用重载决策来选择适当的重载_counteruu.但是,在例如初始化器中a,如果我们要在结束时执行重载决策Test,例如在初始化器中,则选择不会被选择的重载(=声明)d.因此_counteruu,在完成的范围内重新评估时,指的是另一个不同的声明Test.
要显示我所指的是哪些调用,请考虑以下预处理定义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);
};
Run Code Online (Sandbox Code Playgroud)
简化收益率
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);
};
Run Code Online (Sandbox Code Playgroud)
我们可以清楚地看到该机制现在如何工作:由于派生到基础转换的排序方式,当ICount<一些足够大的数字>被传递时,重载分辨率将更喜欢最后添加的重载.但是,初始化程序中的调用a将选择第一个重载; 但重新评估此初始化程序将选择最后一个.
1这个要点也存在于C++ 03中,但在§3.3.6中.