簡體   English   中英

臨時生命周期延長與 clang 上的復制省略對象混合

[英]Temporary lifetime extension mixed with copy elision object on clang

我在我的一個項目中遇到了一個問題,它使用聚合類型以相對安全的方式延長臨時對象的生命周期,方法是使包含引用不可復制和不可移動的聚合,但是強制復制/移動省略(C++17)不在乎如果一個對象是可復制的或可移動的。 這一切都很好,因為在我看來,復制/移動不應該真正發生,因為實際上應該只有一個對象。 在我的例子中,這個對象有一個引用,可以延長一些臨時的生命周期,據我所知,只有當保存引用的聚合被銷毀時,臨時才應該被銷毀。
以下代碼是該問題的簡化示例,請注意此處B確實是可復制的,但它也可能不是,並且將遵循相同的結果。

#include <iostream>

struct K
{
    K() { std::cout << "K::K()" << std::endl; }
    K(K const&) { std::cout << "K::K(K const&)" << std::endl; }
    K(K&&) { std::cout << "K::K(K&&)" << std::endl; }
    ~K() { std::cout << "K::~K()" << std::endl; }
};

struct B
{
    K const& l;
    ~B() { std::cout << "B::~B()" << std::endl; }
};

int main() {
    B b = B{ K{} };
    std::cout << "end of main" << std::endl;
    (void)b;
}

上面的代碼在不同的編譯器中有不同的行為。 MSVC 和 GCC 只會在B b之后銷毀臨時K{} ,而 Clang 將在表達式末尾銷毀K{} 我的問題是:這里顯示的代碼是否調用了 UB? 如果不是,誰是正確的,MSVC 和 GCC 還是 Clang? 這個問題已知嗎?


請注意:要使B在 C++17 中不可復制,只需將復制構造函數聲明為已刪除,它仍將是一個聚合。 在 C++20 中,這又發生了變化(不要問我為什么)!...並且您需要在聚合中包含一個不可復制的成員,如 p1008r1 所示(很好的解決方案!)。

這看起來像 Clang 中的一個錯誤。

強制復制省略B b = B{ K{} }; 應該完全等價於B b{K{}}; 並且將K對象的生命周期延長到b的生命周期適用於那里,因為它是聚合初始化。 不存在其他可能包含首先綁定到臨時K對象的引用的臨時B對象,並且我在生命周期擴展規則中看不到任何可能相關的異常。

有一個例外是通過return語句中的強制復制省略適用的,因此例如返回B{K{}}以分配以初始化B b將無法延長K對象的生命周期,但我認為這很明顯這是行不通的。

通過快速搜索,我在https://github.com/llvm/llvm-project/issues的 LLVM 問題列表中找不到任何匹配的問題。 您可能要考慮報告它。

有一個相關的CWG 問題 1697詢問在 C++17 之前的行為應該是什么,給定可選的復制省略,但由於復制省略被強制要求而關閉。 我不確定 C++17 之前的預期行為是什么。


不過,這聽起來確實有點危險,因為如果有人選擇使用-std=c++14進行編譯,生命周期可能會改變,而且通常聚合初始化的生命周期擴展規則並不明顯。 特別是它不適用於 C++20 帶括號的聚合初始化。

暫無
暫無

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

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