簡體   English   中英

模板類中的編譯時計數器

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM