Avoid creating dangling references in case of nested tuples
authorVille Voutilainen <ville.voutilainen@gmail.com>
Fri, 25 Sep 2015 16:41:45 +0000 (19:41 +0300)
committerVille Voutilainen <ville@gcc.gnu.org>
Fri, 25 Sep 2015 16:41:45 +0000 (19:41 +0300)
for tuple constructors that construct from other tuples.

2015-09-25  Ville Voutilainen  <ville.voutilainen@gmail.com>

Avoid creating dangling references in case of nested tuples
for tuple constructors that construct from other tuples.
* include/std/tuple (_TC::_NonNestedTuple): New.
* include/std/tuple (tuple::_TNTC): New.
* include/std/tuple (tuple(const tuple<_UElements...>&),
tuple(tuple<_UElements...>&&): Use _TNTC.
* testsuite/20_util/tuple/cons/nested_tuple_construct.cc: New.

From-SVN: r228134

libstdc++-v3/ChangeLog
libstdc++-v3/include/std/tuple
libstdc++-v3/testsuite/20_util/tuple/cons/nested_tuple_construct.cc [new file with mode: 0644]

index 24a60508cce981fe1797615d0e759395eabf5398..50f6e8b694c6d53b379605b9c76e0d84755d4da6 100644 (file)
@@ -1,3 +1,13 @@
+2015-09-25  Ville Voutilainen  <ville.voutilainen@gmail.com>
+
+       Avoid creating dangling references in case of nested tuples
+       for tuple constructors that construct from other tuples.
+       * include/std/tuple (_TC::_NonNestedTuple): New.
+       * include/std/tuple (tuple::_TNTC): New.
+       * include/std/tuple (tuple(const tuple<_UElements...>&),
+       tuple(tuple<_UElements...>&&): Use _TNTC.
+       * testsuite/20_util/tuple/cons/nested_tuple_construct.cc: New.
+
 2015-09-24  Jonathan Wakely  <jwakely@redhat.com>
 
        PR libstdc++/67707
index 59b992a4a06a40e8c35bc579b0d41966d0d3fc60..751d7eb97cc238a9cd66329d10f9cf63207d721c 100644 (file)
@@ -486,6 +486,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     {
       return __and_<is_convertible<_UElements&&, _Elements>...>::value;
     }
+
+    template<typename _SrcTuple>
+    static constexpr bool _NonNestedTuple()
+    {
+      return  __and_<__not_<is_convertible<_SrcTuple, _Elements...>>,
+                     __not_<is_constructible<_Elements..., _SrcTuple>>
+              >::value;
+    }
   };
 
   template<typename... _Elements>
@@ -514,6 +522,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     {
       return false;
     }
+
+    template<typename... _UElements>
+    static constexpr bool _NonNestedTuple()
+    {
+      return true;
+    }
   };
 
   /// Primary class template, tuple
@@ -599,40 +613,54 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       constexpr tuple(tuple&&) = default; 
 
-      template<typename... _UElements, typename
+      // Shortcut for the cases where constructors taking tuples
+      // must avoid creating temporaries.
+      template<typename _Dummy> using _TNTC =
+        _TC<is_same<_Dummy, void>::value && sizeof...(_Elements) == 1,
+            _Elements...>;
+
+      template<typename... _UElements, typename _Dummy = void, typename
         enable_if<_TMC<_UElements...>::template
                     _ConstructibleTuple<_UElements...>()
                   && _TMC<_UElements...>::template
-                    _ImplicitlyConvertibleTuple<_UElements...>(),
+                    _ImplicitlyConvertibleTuple<_UElements...>()
+                  && _TNTC<_Dummy>::template
+                    _NonNestedTuple<const tuple<_UElements...>&>(),
         bool>::type=true>
         constexpr tuple(const tuple<_UElements...>& __in)
         : _Inherited(static_cast<const _Tuple_impl<0, _UElements...>&>(__in))
         { }
 
-      template<typename... _UElements, typename
+      template<typename... _UElements, typename _Dummy = void, typename
         enable_if<_TMC<_UElements...>::template
                     _ConstructibleTuple<_UElements...>()
                   && !_TMC<_UElements...>::template
-                    _ImplicitlyConvertibleTuple<_UElements...>(),
+                    _ImplicitlyConvertibleTuple<_UElements...>()
+                  && _TNTC<_Dummy>::template
+                    _NonNestedTuple<const tuple<_UElements...>&>(),
         bool>::type=false>
         explicit constexpr tuple(const tuple<_UElements...>& __in)
         : _Inherited(static_cast<const _Tuple_impl<0, _UElements...>&>(__in))
         { }
 
-      template<typename... _UElements, typename
+      template<typename... _UElements, typename _Dummy = void, typename
         enable_if<_TMC<_UElements...>::template
                     _MoveConstructibleTuple<_UElements...>()
                   && _TMC<_UElements...>::template
-                    _ImplicitlyMoveConvertibleTuple<_UElements...>(),
+                    _ImplicitlyMoveConvertibleTuple<_UElements...>()
+                  && _TNTC<_Dummy>::template
+                    _NonNestedTuple<tuple<_UElements...>&&>(),
         bool>::type=true>
         constexpr tuple(tuple<_UElements...>&& __in)
         : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { }
 
-      template<typename... _UElements, typename
+      template<typename... _UElements, typename _Dummy = void, typename
         enable_if<_TMC<_UElements...>::template
                     _MoveConstructibleTuple<_UElements...>()
                   && !_TMC<_UElements...>::template
-                    _ImplicitlyMoveConvertibleTuple<_UElements...>(),
+                    _ImplicitlyMoveConvertibleTuple<_UElements...>()
+                  && _TNTC<_Dummy>::template
+                    _NonNestedTuple<tuple<_UElements...>&&>(),
         bool>::type=false>
         explicit constexpr tuple(tuple<_UElements...>&& __in)
         : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { }
diff --git a/libstdc++-v3/testsuite/20_util/tuple/cons/nested_tuple_construct.cc b/libstdc++-v3/testsuite/20_util/tuple/cons/nested_tuple_construct.cc
new file mode 100644 (file)
index 0000000..32ef3cc
--- /dev/null
@@ -0,0 +1,60 @@
+#include <string>
+#include <tuple>
+#include <testsuite_hooks.h>
+
+static std::string result;
+
+struct X {
+  int state; // this has to be here
+  X() {
+    result += "Def";
+  }
+
+  X(X const&) {
+    result += "Copy";
+  }
+
+  X(X&&) {
+    result += "Move";
+  }
+
+  ~X() {
+    result += "Dtor";
+  }
+};
+
+void f()
+{
+  X v;
+  std::tuple<X> t1{v};
+  std::tuple<std::tuple<X>&&> t2{std::move(t1)};
+  std::tuple<std::tuple<X>> t3{std::move(t2)};
+}
+
+void f2()
+{
+  X v;
+  std::tuple<X> t1{std::move(v)};
+  std::tuple<std::tuple<X>&&> t2{std::move(t1)};
+  std::tuple<std::tuple<X>> t3{std::move(t2)};
+}
+
+void f3()
+{
+  std::tuple<X> t1{X{}};
+  std::tuple<std::tuple<X>&&> t2{std::move(t1)};
+  std::tuple<std::tuple<X>> t3{std::move(t2)};
+}
+
+int main()
+{
+  f();
+  VERIFY(result == "DefCopyMoveDtorDtorDtor");
+  result = "";
+  f2();
+  VERIFY(result == "DefMoveMoveDtorDtorDtor");
+  result = "";
+  f3();
+  VERIFY(result == "DefMoveDtorMoveDtorDtor");
+  result = "";
+}