![](/img/trans.png)
[英]constexpr array member with template specialization: inconsistent behavior cross compilers
[英]Inconsistent overload resolution for constexpr member functions across compilers
我遇到了一個只在gcc上重現的編譯器錯誤,我把它縮小到一個最小的可重復樣本,它也在msvc上失敗但仍然可以用clang編譯好。 這是代碼:
struct vec { float _x[2]; constexpr float operator[](int index) const { return _x[index]; } float& operator[](int index) { return _x[index]; } }; struct mat { vec _x[2]; constexpr vec operator[](int index) const { return _x[index]; } vec& operator[](int index) { return _x[index]; } }; constexpr float bar(float f) { return f; } constexpr float one(mat const& m) { return m[0][0]; // fails in gcc 5+, msvc } constexpr float two(mat const& m) { return bar(m[0][0]); // fails in gcc 5+ }
據我所知,第24行的vec :: operator []的重載解析不考慮const重載(第5行),因為mat :: operator [] const(第13行)按值返回,而不是通過const引用返回,但是我不確定為什么會阻止考慮vec :: operator [] const。 來自gcc的錯誤消息:
: In function 'constexpr float one(const mat&)': :24:18: error: call to non-constexpr function 'float& vec::operator[](int)' return m[0][0]; // fails in gcc 5+, msvc
從msvc:
(22): error C3615: constexpr function 'one' cannot result in a constant expression (24): note: failure was caused by call of undefined function or one not declared 'constexpr' (24): note: see usage of 'vec::operator []'
原始代碼在msvc中編譯得很好,但樣本沒有,所以我花了一點時間才找到允許它與msvc一起工作的東西。 顯然,通過另一個constexpr函數傳遞返回值會以某種方式迫使msvc考慮const重載,但我不知道這是什么原因。 這是一個錯誤還是一些深奧的語言規則的結果? 哪個編譯器正確?
這里的最后一個問題是,這只是一個問題,因為const重載按值返回,如果它們通過const引用返回,則任何編譯器都沒有錯誤。 在這里返回值是一個無用的悲觀,我應該刪除?
哪個編譯器正確?
在這種情況下,兩個編譯器都是正確的(或者至少是不正確的)。 C ++標准說( [dcl.constexpr] / 5 )任何constexpr函數都不能在常量表達式中調用,這使得程序“形成錯誤;無需診斷”。 這意味着具有該條件的程序不正確,但不需要實現(編譯器)來打印關於它的任何診斷消息。
你正確地認為表達式m[0][0]
首先調用constexpr vec mat::operator[](int) const;
然后調用float& vec::operator[](int);
,問題是這個vec::operator[]
不是constexpr函數。
但我不確定為什么會阻止考慮
vec::operator[] const
。
請注意,任何函數調用(包括可重載運算符)的重載解析僅使用子表達式的類型和值類別,包括非靜態成員函數的隱式類型參數。 特別是在這里,它不考慮表達式是在常量表達式中使用還是在constexpr函數的始終執行的片段中使用。 如果用完全相同的代碼拼寫的兩個表達式涉及根據如何使用該表達式調用完全不同的函數,那將太令人困惑。
在這種情況下, m[0][0]
在常量表達式中不使用時完全有效:
float runtime_func(const mat& m) { return m[0][0]; }
在那個用途中,它調用constexpr vec mat::operator[](int) const;
因為m
的類型是const限定的,然后是float& vec::operator[](int);
因為m[0]
的類型是vec
,而不是const限定的。 因此, one
和two
調用完全相同的功能,兩者都是格式錯誤,無需診斷。
我不能說,為什么MSVC給出了一個錯誤的功能, one
而不是功能two
。 但我不認為它實際上是使用float vec::operator[](int) const;
代替。 我注意到我是否真的嘗試在常量表達式中使用two
,如
constexpr mat m = {{{{1,2}}, {{3,4}}}};
constexpr float val = two(m);
然后MSVC確實在那時提供了一些有用的錯誤消息 。 MSVC錯過了提前標記該問題的機會,可被視為實施質量問題。
在這里返回值是一個無用的悲觀,我應該刪除?
在顯示的代碼中,沒有真正的理由不僅僅使所有四個operator[]
函數constexpr
。 非const
對象可以在常量表達式中使用,只要它們的初始化發生在同一個常量表達式中,並且這可以應用於此處涉及的臨時vec
對象。 (當然,在為這個問題簡化它之前,你在發現問題的更完整的項目中可能會考慮其他一些事情。)
這里的最后一個問題是,這只是一個問題,因為const重載按值返回,如果它們通過const引用返回,則任何編譯器都沒有錯誤。 在這里返回值是一個無用的悲觀,我應該刪除?
這個問題始於錯誤的假設。 這不是“唯一的問題,因為const重載按值返回”。 相反,問題來自mat::operator[]
的const重載,返回非const的東西,這導致編譯器適當地應用vec::operator[]
的非const重載。
如果你要將mat::operator[]
的const
重載更改為以下內容,它仍會按值返回,但gcc的警告會消失。
constexpr const vec operator[](int index) const { return _x[index]; }
這是“ constexpr const
”不冗余的情況之一。 “ constexpr
”符合函數的要求,而“ const
”則限定返回的類型。
另一方面,如果您願意在非const版本中通過引用返回,為什么不在const版本中通過引用返回? 有人已經可以獲得參考,那么通過將副本的低效率拋入const版本可以獲得什么? (我可能期望看到另一種方式:非const返回值,因此不修改成員,但在const版本中返回const引用以提高效率。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.