简体   繁体   中英

std::tuple with reference fails to compile in clang, but not in gcc

The following code:

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

Will fail to compile in clang 9.0 but succeeds with gcc 9.2 : https://godbolt.org/z/6CuEaf

clang will fail with the error:

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};

        ^

Which one is correct here? I don't see anything that should result in exceeding the lifetime of b and c .


Consider the following code:

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;
}   

Its output is as follows:

constructed from 1
0x7fff6a0bb78b
destructed
0x7fff6a0bb78b

Live demo is here .

It kind-of simulates std::tuple with Y here. In Y y(i) , a parameter ref is bound to a temporary of type X . But this temporary is destructed as the lifetime of ref parameter is ended. Then, ref_ becomes a dangling reference, as @rafix07 pointed out in a comment.

It therefore makes no sense to initialize a tuple member of a reference type in such a way that results in a binding to a temporary. The question is whether a compiler is required to issue a diagnostic here. I don't think it has to and I don't see any relevant information in [tuple] .

It just says, regarding forward_as_tuple :

Constructs a tuple of references to the arguments in t suitable for forwarding as arguments to a function. Because the result may contain references to temporary objects, a program shall ensure that the return value of this function does not outlive any of its arguments .

But the same wording is not present by the definition of tuple ("direct") constructor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM