簡體   English   中英

使用“extern模板”時,對模板進行專門化的正確方法是什么?

[英]What's the right way to specialize a template when using “extern template”?

我希望有人可以指出在模板類中專門化方法的正確方法,同時使用“extern模板類”和“模板類”進行gnu c ++的顯式實例化。 我試圖通過模仿我真實問題的最簡單的例子來解決這個問題。 似乎聲明“extern模板”意味着模板實例化,這在專門化方法時會導致錯誤。 給出一個驅動程序:

main.cc

#include A_H
#include <iostream>

int main()
{
    A<int> ai;
    A<long> al;

    std::cout << "ai=" << ai.get() << " al=" << al.get() << std::endl;

    return 0;
}

以及A的以下實現

template<typename T>
struct A
{
    int get() const;
};

extern template class A<int>;
extern template class A<long>;

a.cc

#include "a.h"

template<typename T>
int A<T>::get() const
{
    return 0;
}

template<>
int A<long>::get() const
{
    return 1;
}

template class A<int>;
template class A<long>;

使用g ++ 4.1.2或4.4.4編譯時收到以下錯誤

 % g++ -Wall -g -D'A_H="a.h"' a.cc main.cc          
a.cc:10: error: specialization of 'int A<T>::get() const [with T = long int]' after instantiation
 %

如果我在ah中注釋掉兩個“extern模板”行,那么兩個編譯器都會按預期編譯和工作。 我假設在沒有“extern模板”的情況下存在明確的實例化,即使在C ++ 0x中也是未指定的行為,否則,C ++ 0x添加“extern模板”的重點是什么?

如果我改為將A實現為:

A-hack.h

template<typename T>
struct A
{
    int get() const;
};

template<typename T>
int A<T>::get() const
{
    return 0;
}

template<>
inline
int A<long>::get() const
{
    return 1;
}

extern template class A<int>;
extern template class A<long>;

a-hack.cc

#include "a-hack.h"

template class A<int>;
template class A<long>;

並再次編譯,這按預期工作

% g++ -Wall -g -D'A_H="a-hack.h"' a-hack.cc main.cc
% ./a.out 
ai=0 al=1

但是,在我的真實示例中,這會導致程序崩潰,使用g ++ 4.1.2(在使用g ++ 4.4.4時)。 我沒有縮小崩潰的確切原因(分段錯誤)。 它看起來好像堆棧指針在對A <> :: get()的調用中被破壞了。

我意識到顯式模板實例化在這一點上是非標准的,但是有人會期望我上面所做的工作嗎? 如果沒有,這樣做的正確方法是什么?

謝謝

extern template class A<long>;

這一行說明A<long>根據編譯器已經看到的定義顯式實例化。 稍后添加專業化時,您會破壞其含義。

將您的專業化聲明添加到頭文件中。

template <typename T> struct A { /*...*/ };
template<> int A<long>::get() const;
extern template class A<int>;
extern template class A<long>;

通常,最好在與主模板相同的頭文件中放置盡可能多的特化聲明,以減少編譯器關於應該對任何特定實例使用哪個聲明的意外。


請注意,如果您正在處理單個模板實體,則不需要extern template聲明(與此情況相反,我們必須向編譯器指示 A<long>函數 A<long>::get() )。 如果要在另一個翻譯單元中專門化一個功能模板,只需編寫template<>

template<typename T> int freeGet() { return 0; }  // you can even add "inline" here safely!
template<> int freeGet<long>();  // this function is not inline (14.7.3/12)

但你必須有<> 如果省略<> ,聲明將變為默認實現的顯式實例化( return 0 ),這可能不是您想要的! 即使您添加extern ,也允許編譯器內聯該默認實現; 如果你的代碼在傳遞-O2時意外中斷,你可能不小心忽略了<>某處。

添加此答案以解決標題中的問題(模板實例化,而不一定是模板方法實例化)。

這非常類似於函數聲明/定義。

  • 顯式實例化聲明: extern template class是一個聲明,通常應該放在標題中。
  • 顯式實例化定義: template class是一個定義,通常應該放在cpp中。
  • 聲明后的目標文件中沒有生成代碼。
  • 代碼在包含該定義的cpp的目標文件中生成。
  • 如果沒有顯式實例化,則在實際使用模板時將發生隱式實例化。 這將發生在使用該模板的每個編譯單元(目標文件)中。
  • 如果遇到聲明,則不會發生隱式實例化。 這是這種機制的要點 - 避免因隱式實例化而導致的目標代碼重復,因為編譯器不相信有一個編譯單元負責實例化模板。

更多信息在這里

暫無
暫無

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

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