我的實際問題是複雜得多,看起來很難給出一個簡短的具體示例來重現它。所以我在這裏發帖不同的小例子,可能是相關的,並且它的討論可以在實際問題有所幫助:嵌套std :: forward_as_tuple和分段錯誤
// A: works fine (prints '2')
cout << std::get <0>(std::get <1>(
std::forward_as_tuple(3, std::forward_as_tuple(2, 0)))
) << endl;
// B: fine in Clang, segmentation fault in GCC with -Os
auto x = std::forward_as_tuple(3, std::forward_as_tuple(2, 0));
cout << std::get <0>(std::get <1>(x)) << endl;
的實際問題不涉及std::tuple
,所以做出的例子獨立的,這裏有一個定製,最小大致相當於:
template <typename A, typename B>
struct node { A a; B b; };
template <typename... A>
node <A&&...> make(A&&... a)
{
return node <A&&...>{std::forward <A>(a)...};
}
template <typename N>
auto fst(N&& n)
-> decltype((std::forward <N>(n).a))
{ return std::forward <N>(n).a; }
template <typename N>
auto snd(N&& n)
-> decltype((std::forward <N>(n).b))
{ return std::forward <N>(n).b; }
根據這些定義,我得到完全相同的行爲:
// A: works fine (prints '2')
cout << fst(snd(make(3, make(2, 0)))) << endl;
// B: fine in Clang, segmentation fault in GCC with -Os
auto z = make(3, make(2, 0));
cout << fst(snd(z)) << endl;
一般來說,APPEA rs的行爲取決於編譯器和優化級別。我無法通過調試找到任何東西。看來在所有情況下,所有內容都被內聯和優化,所以我無法弄清楚導致問題的特定代碼行。
如果臨時表只要存在對它們的引用(並且我沒有從函數體內返回對局部變量的引用),就沒有看到上述代碼可能導致問題的根本原因和爲什麼案例A和B應該有所不同。
在我的實際問題中,即使對於單線版本(情況A),Clang和GCC都給出了分割錯誤,並且不管優化級別如何,所以問題相當嚴重。
使用值或rvalue引用(例如,在自定義版本中爲std::make_tuple
或node <A...>
)時,問題消失。當元組不嵌套時,它也會消失。
但是以上都沒有幫助。我正在實現的是一種用於視圖的表達式模板,可以對許多結構進行延遲評估,包括元組,序列和組合。所以我肯定需要對臨時對象的右值引用。一切工作正常嵌套元組,例如對於嵌套操作的表達式,例如(a, (b, c))
u + 2 * v
,但不是兩者。
我希望能夠幫助理解上面的代碼是否有效,如果預計會出現分段錯誤,如何避免它,以及編譯器和優化級別會發生什麼。
非常感謝您的回覆。這真是太神奇了,我甚至都沒有想過這種調試......總之,我有幾個問題,一次一個:(1)'int && x = 3怎麼樣; cout << x << endl;'?這是「有限的情況」還是未定義的?因爲它不僅工作正常,而且我也在Stroustrup的書中看到了它。如果它是有效的,那麼我的例子有什麼不同?是不是現在這個'int &&'在另一個結構中?我不明白爲什麼它會有所不同。 – iavr
問題(2):我在想,儘管更復雜,但我的'實際問題'很可能是由於這個問題(儘管程序在單一表達式上崩潰),並且您的解決方案似乎是合理的。但是,我關心的不僅僅是左值引用;它也是關於非引用類型的,在這種情況下,它將按值傳遞和存儲。這些可能很大,並會在單個表達式中傳遞/複製/移動數次。因此,除了在免費商店中存儲數據並移動構造函數的對象之外,我是否僅僅依靠副本來避免複製? – iavr
用延長壽命的東西擴大了答案。總之(1)是的,那是「有限的情況」。它與您的示例不同之處在於它將臨時性直接綁定到引用,而不是對象的引用成員。 (2)是的,如果您通過右值引用傳遞昂貴移動的對象,將會代價高昂。如果你不能避免這種情況,你可以通過回到純引用來改變設計,並引入另一個「存儲」類,當從引用元組分配時,該類會吞噬右值。 – Casey