[英]Reference member binds to a temporary object whose life-time would be shorter than the lifetime of the constructed object
[英]How would auto&& extend the life-time of the temporary object?
下面的代碼說明了我的擔憂:
#include <iostream>
struct O
{
~O()
{
std::cout << "~O()\n";
}
};
struct wrapper
{
O const& val;
~wrapper()
{
std::cout << "~wrapper()\n";
}
};
struct wrapperEx // with explicit ctor
{
O const& val;
explicit wrapperEx(O const& val)
: val(val)
{}
~wrapperEx()
{
std::cout << "~wrapperEx()\n";
}
};
template<class T>
T&& f(T&& t)
{
return std::forward<T>(t);
}
int main()
{
std::cout << "case 1-----------\n";
{
auto&& a = wrapper{O()};
std::cout << "end-scope\n";
}
std::cout << "case 2-----------\n";
{
auto a = wrapper{O()};
std::cout << "end-scope\n";
}
std::cout << "case 3-----------\n";
{
auto&& a = wrapper{f(O())};
std::cout << "end-scope\n";
}
std::cout << "case Ex-----------\n";
{
auto&& a = wrapperEx{O()};
std::cout << "end-scope\n";
}
return 0;
}
看到它住在這里 。
據說auto&&
會延長臨時對象的壽命,但我找不到此規則的標准字詞,至少在N3690中找不到。
最相關的可能是關於臨時對象的12.2.5節,但不完全是我要尋找的對象。
那么,auto &&生命周期擴展規則是適用於表達式中涉及的所有臨時對象還是僅適用於最終結果?
更具體地說,在情況1到達作用域末端之前, a.val
保證有效(非懸掛)?
編輯:我更新了示例以顯示更多案例(3和Ex)。
您將看到只有在情況1中,O的壽命才會延長。
引用const
方式相同:
const auto& a = wrapper{O()};
要么
const wrapper& a = wrapper{O()};
或者也
wrapper&& a = wrapper{O()};
更具體地說,在情況1到達作用域末端之前,
a.val
保證有效(非懸掛)?
是的。
這里的auto
幾乎沒有什么特別重要的。 它只是由編譯器推斷出的正確類型( wrapper
)的占位符 。 要點是,臨時對象綁定到引用。
有關更多詳細信息,請參見我引用的“最重要const”候選人 :
通常,臨時對象僅持續到其出現的完整表達式的結尾。 但是,C ++故意指定將臨時對象綁定到堆棧上const的引用會延長臨時對象的壽命至引用本身的壽命
本文是關於C ++ 03的,但該參數仍然有效:可以將臨時綁定到對const
的引用(但不能綁定到對non- const
的引用)。 在C ++ 11中,臨時變量也可以綁定到右值引用。 在這兩種情況下,臨時項的壽命都延長到參考的壽命。
C ++ 11標准的相關部分正是OP中提到的部分,即12.2 p4和p5:
4-在兩種情況下,臨時變量在與完整表達式末尾不同的位置被銷毀。 第一個上下文是[...]
5-第二種情況是引用綁定到臨時項時。 [...]
(這些行后面的項目符號中有一些例外。)
更新 :(根據texasbruce的評論。)
情況2中的O
壽命短的原因是我們有auto a = wrapper{O()};
(請參見,此處沒有&
),然后該臨時對象就不會綁定到引用。 實際上,臨時文件是使用編譯器生成的copy-constructor復制到a
。 因此,臨時項不會延長其生存期,並且會在出現的完整表達式的末尾死亡。
在此特定示例中存在危險,因為wrapper::val
是引用。 編譯器生成的wrapper
copy-constructor會將a.val
綁定到與臨時val
成員綁定到的同一對象。 該對象也是臨時的,但類型為O
然后,當后一個臨時對象死亡時,我們在屏幕上看到a.val
~O()
,並且a.val
懸掛了!
與此進行對比的情況2:
std::cout << "case 3-----------\n";
{
O o;
auto a = wrapper{o};
std::cout << "end-scope\n";
}
輸出是(當使用gcc使用選項-fno-elide-constructors
編譯時)
case 3-----------
~wrapper()
end-scope
~wrapper()
~O()
現在,臨時wrapper
將其val
成員綁定到o
。 請注意, o
不是臨時的。 如我所說, a
是wrapper
臨時文件的副本,而a.val
也綁定到o
。 在作用域結束之前,臨時wrapper
死亡,我們在屏幕上看到第一個~wrapper()
。
然后范圍結束,我們得到end-scope
。 現在, a
和o
必須在施工相反的順序被破壞,因此,我們看到~wrapper()
時, a
模具和最后~O()
時,它的o
的時間。 這表明a.val
不會a.val
。
(最后再說一句:我已經使用-fno-elide-constructors
,防止復制建設,將這里的討論復雜化相關的一個優化但這是另一個故事 。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.