[英]Linker error for constexpr static member variable in gcc and clang
我有一個片段:
enum class EC {a, b};
struct B {
constexpr B(EC ec): ec_(ec) {}
EC ec_;
};
struct A_base {
constexpr A_base(B b): b_(b) { }
B b_;
};
struct A: A_base {
static constexpr B bbb = EC::a;
constexpr A(B bbbb): A_base(bbbb) { }
};
int main()
{
A a1(A::bbb); // 1
A a2{A::bbb}; // 2
A a3 = A::bbb; // 3
A a4 = {A::bbb}; // 4
}
它通過支持c ++ 17的現代編譯器編譯好。 使用c ++ 11和c ++ 14標准支持鏈接器發生錯誤。 這個問題已在Linker錯誤(未定義的引用)中討論過`static constexpr const char *`和perfect-forwarding , C ++ Linker Error With Class static constexpr和其他一些討論。 我理解為什么會出現這種錯誤。
但有些事情我不明白:
編譯器與關閉優化有何不同是正常的,為什么gcc將a1,a2初始化不等同於a3,a4?
A
的所有初始化都調用A::A(B )
構造函數,它需要復制一個B
,它使用編譯器生成的B::B(B const&)
。 將變量綁定到引用( A::bbb
)是該變量的使用。 來自[basic.def.odr]的具體規則是:
變量x的名稱顯示為潛在評估的表達式ex,除非將lvalue-to-rvalue轉換(4.1)應用於x,否則會產生一個不調用任何非平凡函數的常量表達式(5.20)。 ,如果x是一個對象,則ex是表達式e的潛在結果集的一個元素,其中左值到右值的轉換(4.1)應用於e,或者e是丟棄值表達式(第5條) )。
對復制構造函數的第一次調用將涉及將A::bbb
綁定到引用,該引用既不是左值到右值的轉換,也不是丟棄值表達式,因此它使用的是odr。
最重要的規則是:
每個程序應該只包含每個非內聯函數或變量的一個定義,該函數或變量在廢棄語句之外的程序中使用(6.4.1); 無需診斷。
A::bbb
是odr,但缺乏定義,因此我們違反了該規則 - 通常稱為odr違規。 但由於在這種情況下編譯器不需要發出診斷(“無需診斷”或簡稱NDR),因此程序是未定義的行為。 對於程序員來說,這些類型的問題有時令人沮喪,但編譯器診斷它們是任意的 - 所以這是我們必須要忍受的。
很可能在更高的優化級別上,編譯器只是忽略副本,因此不需要為A::bbb
調用B::B(B const&)
...至於為什么不同的初始化被區別對待? 可能是由於不同的優化過程。 最終,它不會改變這是一個odr違規的事實 - 無論代碼是否編譯和鏈接。
作為p0386的結果, static constexpr
數據成員是隱式內聯的,這意味着現在A::bbb
確實有一個定義,現在沒有odr違規。 C ++ 17很酷。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.