2017-02-14 82 views
1

我知道,有很多關於靜態(constexpr)成員鏈接的回答問題。模板類的ODR與靜態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))。

我的問題給你:

  1. 是將它保存到使用模板把戲?缺點是什麼?
  2. 對於像b這樣的情況,放寬odr的定義會有用嗎?

提前致謝!

+1

「*難道是放鬆ODR這樣的情況下,像b在未來的定義有用*?」早在C++ 17:constexpr靜態數據成員是隱式「內聯」的。 – ildjarn

+0

可愛!不能等待它。 :) – Viatorus

回答

3

參見[basic.def.odr/6:

There can be more than one definition of a class type (Clause 9), enumeration type (7.2), inline function with external linkage (7.1.2), class template (Clause 14), non-static function template (14.5.6), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. ...

此規則的效果是每個模板聲明的行爲就好像它是內聯。 (但它不會擴展到顯式實例化明確分工聲明。)

在第一個片段,你有

template<typename T> 
constexpr std::array<int, 1> Foobar<int, T>::a; 

這是一個模板聲明,因此允許被多重定義。在第二個片段,你有

constexpr std::array<int, 1> Foobar<int>::a; 

這不是模板聲明:這個定義本身是沒有模板,即使被定義的東西恰好是模板的一個特例。

My question to you:

  1. Is it save to use the template trick? What are the disadvantage?

這裏沒有「竅門」。如果你想定義成員全部Foo<T>,那麼你別無選擇,只能將定義放在頭文件中。如果你想爲定義一個特定的成員Foo<T>Foo<int>,那麼你不能把定義放在頭文件中(直到C++ 17,它引入了內聯變量)。沒有什麼竅門,因爲你所假設的要做什麼取決於你的具體目標。

(你的第二個問題是回答的評論部分)。