簡體   English   中英

防止模板化類的多次實例化

[英]Prevent multiple instantiations of templated class

我有模板類,說:

template <int X>
struct Foo {
  static int get() {return X;}
};

我當然可以顯式實例化我想要的版本:

template class Foo<1>;

如果嘗試第二次顯式實例化,我想在編譯時生成錯誤。

template class Foo<1>;
template class Foo<2>; // How to error here at compile time?

這可能嗎?

我懷疑如果編譯是在多個翻譯單元中完成的,這將需要使用一些“重新定義”技巧來讓鏈接器捕捉到這一點。 我一生都無法弄清楚這是否可能,或者如何做到。

如果有辦法做到這一點,它是否可以在沒有顯式模板實例化的情況下工作?


語境

我正在編寫一個完全靜態的類庫來管理我正在使用的微控制器上的一些硬件。 我想讓更改編譯時參數 ( X ) 變得容易,因此我使用模板。 #define是不可接受的。 constexpr不起作用,您將如何#include依賴源文件?

具體來說,我有一個只能運行一次的init()函數,我實際上是在使用__attribute__((constructor))來強制它在main()之前為我運行。 如果庫的其他用戶無意中實例化了第二個實例,就會發生不好的事情。

我很確定跨翻譯單元這樣做是不可能的,因為您必須從模板中生成一個非弱符號。 在翻譯單元內,這很容易:

template<int>
struct Foo {
  friend void highlander();
};

在一個翻譯單元中重復實例化Foo是不正確的,因為highlander只能有一個定義。 (方便的是,不可能引用該函數,因為它僅對 ADL 可見並且沒有參數。)

當然,您也可以通過為每個專業化提供不同的返回類型來使其跨翻譯單元格式錯誤,但是那時不需要診斷,並且在實踐中您不會得到任何診斷。

您可以將類模板封裝為私有嵌套類模板,然后僅將您要創建的實例公開為公共成員:

class Foo {
private:
    template<int X>
    struct BarImpl {
        static int get() { return X; }
    };

public:
    using Bar = BarImpl<1>;
};

然后你可以像這樣使用它:

int i = Foo::Bar::get();
//int j = Foo::BarImpl<2>::get(); // Error, BarImpl is private

此外,既然你知道只會創建一個模板實例,並且你知道哪一個,你可以(但當然,不必)將.hpp和.cpp中的模板的聲明和定義分開。像這樣的文件:

// Foo.hpp
class Foo {
private:
    template<int X>
    struct BarImpl {
        static int get();
    };

    static constexpr int Y = 1;
public:
    using Bar = BarImpl<Y>;
};

// Foo.cpp
template<>
int Foo::Bar::get() {
    return Y;
}

由於無法通過Foo::Bar訪問X我們必須將參數保存在某處(此處為Y )。 如果你不能使用constexpr你就可以使它成為const 這也具有命名參數的優勢,而不僅僅是具有“魔術值”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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