![](/img/trans.png)
[英]Initialize static constexpr member variable of class template
[英]ODR of template class with static constexpr member
我知道,關於靜態(constexpr)成員的鏈接有很多回答的問題。
但我想知道,為什么使用模板類的外部定義在頭文件中工作,但不適用於專門的類。
a)這沒有鏈接器錯誤:
template<typename, typename>
struct Foobar;
template<typename T>
struct Foobar<int, T> {
static constexpr std::array<int, 1> a = {{1}};
};
template<typename T>
constexpr std::array<int, 1> Foobar<int, T>::a;
// foo.cpp
std::cout << Foobar<int, int>::a[0] << "\n";
// bar.cpp
std::cout << Foobar<int, int>::a[0] << "\n";
objdump:
foo.o: 0000000000000000 w O .rodata._Z6FoobarIiiE1aE 0000000000000004 _Z6FoobarIiiE1aE
bar.o: 0000000000000000 w O .rodata._Z6FoobarIiiE1aE 0000000000000004 _Z6FoobarIiiE1aE
鏈接文件: 0000000000475a30 w O .rodata 0000000000000004 _Z6FoobarIiiE1aE
b)這不是(多重定義):
template<typename>
struct Foobar;
template<>
struct Foobar<int> {
static constexpr std::array<int, 1> a = {{1}};
};
constexpr std::array<int, 1> Foobar<int>::a;
// foo.cpp
std::cout << Foobar<int>::a[0] << "\n";
// bar.cpp
std::cout << Foobar<int>::a[0] << "\n";
objdump:
foo.o 0000000000000100 g O .rodata 0000000000000004 _Z6FoobarIiE1aE
bar.o: 0000000000000420 g O .rodata 0000000000000004 _Z6FoobarIiE1aE
我們看到,外部定義在目標文件中有不同的地址(例子b))。
我向你提問:
先感謝您!
見[basic.def.odr] / 6:
類類型(第9節),枚舉類型(7.2),帶內部鏈接的內聯函數(7.1.2),類模板(第14節),非靜態函數模板(14.5.6)可以有多個定義,類模板的靜態數據成員(14.5.1.3),類模板的成員函數(14.5.1.1),或者在程序中未指定某些模板參數(14.7,14.5.5)的模板特化,前提是每個模板定義出現在不同的翻譯單元中,並且定義滿足以下要求。 ...
此規則的作用是每個模板聲明的行為都像是內聯的。 (但它不會擴展到顯式實例化和顯 式特化聲明。)
在第一個片段中,你有
template<typename T>
constexpr std::array<int, 1> Foobar<int, T>::a;
這是一個模板聲明 ,因此可以進行多重定義。 在第二個片段中,你有
constexpr std::array<int, 1> Foobar<int>::a;
這不是模板聲明 :定義本身不是模板化的,即使被定義的東西恰好是模板的特化。
我向你提問:
- 是否保存使用模板技巧? 有什么缺點?
這里沒有“詭計”。 如果要為所有 Foo<T>
定義成員,那么您別無選擇,只能將定義放在頭文件中。 如果要為一個特定的 Foo<T>
定義成員,例如Foo<int>
,則不能將該定義放在頭文件中(直到C ++ 17,它引入了內聯變量。)沒有技巧,因為你應該做什么取決於你的具體目標。
(你的第二個問題在評論部分得到了解答。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.