簡體   English   中英

為什么在 C++20 之前 std::swap 沒有標記為 constexpr ?

[英]Why isn't std::swap marked constexpr before C++20?

在 C++20 中, std::swap變成了constexpr函數。

我知道標准庫在標記事物constexpr方面確實落后於語言,但到 2017 年, <algorithm>和其他一些東西一樣,幾乎都是 constexpr。 然而 - std::swap不是。 我依稀記得有一些奇怪的語言缺陷阻止了那個標記,但我忘記了細節。

有人可以簡潔明了地解釋這一點嗎?

動機:需要理解為什么在 C++11/C++14 代碼中標記一個std::swap()的函數constexpr可能是個壞主意。

奇怪的語言問題是CWG 1581

第 15 條 [特殊] 非常清楚,特殊成員函數僅在 odr 使用時才隱式定義。 這給未計算上下文中的常量表達式帶來了問題:

 struct duration { constexpr duration() {} constexpr operator int() const { return 0; } }; // duration d = duration(); // #1 int n = sizeof(short{duration(duration())});

這里的問題是我們不允許在這個程序中隱式定義constexpr duration::duration(duration&&) ,所以初始化列表中的表達式不是一個常量表達式(因為它調用了一個尚未定義的 constexpr 函數),所以括號初始化器包含一個收縮轉換,所以程序是格式錯誤的。

如果我們取消注釋第 1 行,則隱式定義了移動構造函數並且程序有效。 這種遠距離的詭異動作是極其不幸的。 在這一點上,實現存在分歧。

您可以閱讀問題描述的其余部分。

2017 年在阿爾伯克基的P0859中(在 C++17 發布后)通過了針對此問題的解決方案。 這個問題是一個阻礙,因為它們都能夠有一個constexpr std::swap (在P0879 中解決)和一個constexpr std::invoke (在P1065 中解決,它也有 CWG1581 示例),兩者都適用於 C++20。


在我看來,這里最容易理解的示例是 P1065 中指出的 LLVM 錯誤報告中的代碼:

 template<typename T> int f(T x) { return x.get(); } template<typename T> constexpr int g(T x) { return x.get(); } int main() { // OK The body of `f' is not required. decltype(f(0)) a; // Seems to instantiate the body of `g' // and results in an error. decltype(g(0)) b; return 0; }

CWG1581 是關於何時定義 constexpr 成員函數的,並且決議確保它們僅在使用時被定義。 在 P0859 之后,以上是格式良好的( b的類型是int )。

由於std::swapstd::invoke都必須依賴於檢查成員函數(前者中的移動構造/賦值和后者中的調用運算符/代理調用),因此它們都取決於此問題的解決方案。

原因

(由於@NathanOliver)

要允許constexpr交換函數,您必須檢查 - 在實例化此函數的模板之前 - 交換的類型是可移動構造和可移動分配的。 不幸的是,由於僅在 C++20 中解決了語言缺陷,您無法檢查它,因為就編譯器而言,相關成員函數可能尚未定義。

年表

  • 2016 年:Antony Polukhin 提交提案P0202 ,將所有<algorithm>函數標記為constexpr
  • 標准委員會核心工作組討論缺陷CWG-1581 這個問題使得constexpr std::swap()constexpr std::invoke() - 請參閱上面的解釋。
  • 2017 年:安東尼修改了他的提議幾次以排除std::swap和其他一些結構,這被 C++17 接受。
  • 2017 年:CWG-1581 問題的決議作為P0859提交,並於 2017 年被標准委員會接受(但在 C++17 發布之后)。
  • 2017 年底:安東尼提交補充提案P0879 ,在 CWG-1581 決議后使std::swap() constexpr 。
  • 2018 年:補充提案被接受(?)到 C++20 中。 正如巴里指出的那樣, constexpr std::invoke()修復也是如此。

您的具體情況

如果您檢查移動可構造性和移動可分配性,而是直接檢查某些其他類型的特性,特別是確保這一點,則可以使用constexpr交換。 例如,只有原始類型,沒有類或結構。 或者,理論上,您可以放棄檢查並只處理您可能遇到的任何編譯錯誤,以及編譯器之間的不穩定行為切換。 無論如何,不​​要用那種東西替換std::swap()

暫無
暫無

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

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