【问题标题】:std::tuple with reference fails to compile in clang, but not in gcc带引用的 std::tuple 在 clang 中编译失败,但在 gcc 中编译失败
【发布时间】:2019-11-11 14:21:18
【问题描述】:

以下代码:

using input_t = std::tuple<short, int&, const long&, const double>;
int b = 1;
int c = 2;
input_t t{0, b, c, 3};

clang 9.0 中编译失败,但在gcc 9.2 中编译成功:https://godbolt.org/z/6CuEaf

clang 将失败并出现错误:

In file included from <source>:2:

tuple:133:17: error: reference member '_M_head_impl' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object

        : _M_head_impl(std::forward<_UHead>(__h)) { }

                       ^~~~~~~~~~~~~~~~~~~~~~~~~

/tuple:218:4: note: in instantiation of function template specialization 'std::_Head_base<2, const long &, false>::_Head_base<int &>' requested here

      _Base(std::forward<_UHead>(__head)) { }

      ^

/tuple:217:4: note: in instantiation of function template specialization 'std::_Tuple_impl<2, const long &, const double>::_Tuple_impl<int &, int, void>' requested here

    : _Inherited(std::forward<_UTail>(__tail)...),

      ^

/tuple:217:4: note: in instantiation of function template specialization 'std::_Tuple_impl<1, int &, const long &, const double>::_Tuple_impl<int &, int &, int, void>' requested here

/tuple:627:11: note: in instantiation of function template specialization 'std::_Tuple_impl<0, short, int &, const long &, const double>::_Tuple_impl<int, int &, int &, int, void>' requested here

    : _Inherited(std::forward<_UElements>(__elements)...) { }

      ^

<source>:10:13: note: in instantiation of function template specialization 'std::tuple<short, int &, const long &, const double>::tuple<int, int &, int &, int, true>' requested here

input_t t{0, b, c, 3};

        ^

这里哪一个是正确的?我没有看到任何应该导致超过 bc 的生命周期的东西。


【问题讨论】:

  • 你想如何将int绑定到long?临时实例是通过将int 提升为long 创建的,然后这个temp 绑定到const long&amp;。你有悬空的参考。将这一行添加到代码中:std::cout &lt;&lt; &amp;c &lt;&lt; std::endl;std::cout &lt;&lt; &amp;std::get&lt;2&gt;(t) &lt;&lt; std::endl; 您将看到两个不同的地址,但两者应该相同。当c 很长时,这些地址将具有相同的值。
  • @rafix07 啊啊啊啊!完美的。那么GCC在这里错了吗?我想是的。
  • @rafix07 完全正确,但它并没有告诉您编译器是否应该在编译时拒绝代码。只要不直接使用std::get&lt;2&gt;(t),就没有UB。

标签: c++ g++ language-lawyer clang++


【解决方案1】:

考虑以下代码:

struct X
{
   X(int i)
   {
      std::cerr << "constructed from " << i << std::endl;
   }
   ~X() { std::cerr << "destructed\n"; }
};

struct Y
{
   const X& ref_;
   Y(const X& ref) : ref_(ref)
   {
      std::cerr << &ref << std::endl;
   }
};

int main ()
{
   int i = 1;
   Y y(i);
   std::cerr << &y.ref_ << std::endl;
}   

它的输出如下:

constructed from 1
0x7fff6a0bb78b
destructed
0x7fff6a0bb78b

现场演示是here

这里有点模拟std::tupleY。在Y y(i) 中,参数ref 绑定到X 类型的临时变量。但是随着ref 参数的生命周期结束,这个临时的被破坏了。然后,ref_ 成为悬空引用,正如 @rafix07 在评论中指出的那样。

因此,以导致绑定到临时对象的方式初始化引用类型的元组成员是没有意义的。问题是编译器是否需要在这里发出诊断。我认为没有必要,我在[tuple] 中没有看到任何相关信息。

它只是说,关于forward_as_tuple

构造一个对t 中参数的引用元组,适合作为参数转发给函数。 由于结果可能包含对临时对象的引用,因此程序应确保此函数的返回值不会超过它的任何参数

但是tuple(“直接”)构造函数的定义不存在相同的措辞。

【讨论】:

  • 你在这里说的是真的,但是,clang 编译这个例子没有问题,所以这不能回答实际问题,这可能是tuple-specific。
  • @Holt 同意,我也意识到了这一点。如果我发现任何相关信息,将尝试更深入地挖掘并更新我的答案。
猜你喜欢
  • 1970-01-01
  • 2021-10-07
  • 1970-01-01
  • 2012-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多