[英]Compile-time counter in template class
我有一個編譯時計數器,我使用多年,受這些答案的啟發。 它適用於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_)>)
}
以下測試編譯並完美運行 (預期輸出為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 Test
,並在main
test<Test<42> >()
>>> test<Test<42> >()
), 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>); // ^ ^
刪除帶下划線的括號可以解決問題。
對於gcc:在sizeof
之前移動+2
是唯一的解決方法
遺憾的是,當包含在宏中時,這些變通辦法似乎無法工作。 這就像編譯器忘記了一段時間后如何計算sizeof的結果......
為什么會這樣? 我做錯了什么,或者只是編譯器錯誤(因為clang和gcc甚至不報告相同的行)?
注意:我知道這個計數器有一個gcc錯誤 。 問題不在於這個錯誤。
您的代碼格式錯誤,無需診斷。 §3.3.7/ 1,第二點要點1 :
在類
S
使用的名稱N
應在其上下文中引用相同的聲明,並在完成的S
范圍內重新評估。 違反此規則無需診斷。
您使用重載_counteruu
來選擇_counteruu
的適當重載。 但是,在例如a
的初始化程序中,如果我們要在Test
的末尾執行重載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);
};
簡化收益率
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<
某個足夠大的數字 >
被傳遞時,由於導出到基礎轉換的排序方式,重載ICount<
將更喜歡最后添加的重載。 然而,在初始化調用a
會選擇第一個過載; 但重新評估此初始化程序將選擇最后一個。
1這個要點也存在於C ++ 03中,但在§3.3.6中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.