我的实际问题要复杂得多,在这里给出一个简短的具体示例来重现它似乎非常困难。所以我在这里发布一个可能相关的不同小例子,它的讨论也可能有助于解决实际问题:
// 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;
一般来说,行为似乎取决于编译器和优化级别。我无法通过调试找到任何东西。似乎在所有情况下,所有内容都是内联和优化的,所以我无法弄清楚导致问题的特定代码行。
如果临时对象只要有对它们的引用就应该存在(并且我没有从函数体内返回对局部变量的引用),我看不出上面的代码可能导致问题的任何根本原因以及为什么情况 A 和B 应该不同。
在我的实际问题中,即使对于单行版本(案例 A),无论优化级别如何,Clang 和 GCC 都会出现分段错误,因此问题非常严重。
当使用值代替或右值引用(例如std::make_tuple
,或node <A...>
在自定义版本中)时,问题就会消失。当元组没有嵌套时,它也会消失。
但以上都没有帮助。我正在实现的是一种用于视图的表达式模板和对许多结构的惰性求值,包括元组、序列和组合。所以我绝对需要对临时对象的右值引用。对于嵌套元组,一切都很好,例如(a, (b, c))
,对于具有嵌套操作的表达式,例如u + 2 * v
,但不是两者兼而有之。
我将不胜感激任何有助于理解上述代码是否有效、是否预计会出现分段错误、如何避免它以及编译器和优化级别可能发生的情况的评论。