[v3 PATCH, RFC] Implement LWG 2729 for tuple

Ville Voutilainen ville.voutilainen@gmail.com
Tue Aug 30 07:58:00 GMT 2016


First, pardons all around if I'm completely repeating what Marc
already tried to do.
I think I'm taking a different approach.

I'm not adding any defaulted or deleted functions, so I don't think I'm changing
triviality. Chances are I need to actually delete the copy operations I want to
get rid of, though; currently they are defined with a conditional
parameter type.
That leaves open the possibility of a move operation being implicitly defaulted
rather than suppressed, which can make them trivial where they weren't before.

I'm not sure whether this has abi impact. In the sense that this is making
previously-defined move operations suppressed, maybe. I guess I could
alternatively try making them conditional, and making the inverse of the
conditional private; that should keep the traits working and retain abi
compatibility, because I don't think our toolchains at least on non-Windows
platforms include access levels in mangling.

Tested for tuple on Linux-x64. Thoughts?

2016-08-30  Ville Voutilainen  <ville.voutilainen@gmail.com>

    Implement LWG 2729 for tuple.
    * include/std/tuple (_Tuple_impl(_Tuple_impl&&)):
    Suppress conditionally.
    (_Tuple_impl(_Tuple_impl<_Idx, _UHead, _UTails...>&&)): Likewise.
    (__is_tuple_impl_trait_impl, __is_tuple_impl_trait): New.
    (_Tuple_impl(const _Head&)): Constrain.
    (_Tuple_impl(_UHead&&)): Likewise.
    (_Tuple_impl(_Tuple_impl&&)): Suppress conditionally.
    (_Tuple_impl(const _Tuple_impl<_Idx, _UHead>&)): Constrain.
    (_Tuple_impl(_Tuple_impl<_Idx, _UHead>&&)): Likewise.
    (operator=(const tuple&)): Enable conditionally.
    (operator=(tuple&&)): Suppress conditionally.
    (operator=(const tuple<_UElements...>&)): Constrain.
    (operator=(tuple<_UElements...>&&)): Likewise.
    (operator=(const tuple&)): Enable conditionally (2-param tuple).
    (operator=(tuple&&)): Suppress conditionally (2-param tuple).
    (operator=(const tuple<_U1, _U2>&)): Constrain.
    (operator=(tuple<_U1, _U2>&&)): Likewise.
    (operator=(const pair<_U1, _U2>&)): Likewise.
    (operator=(pair<_U1, _U2>&&)): Likewise.
    * testsuite/20_util/tuple/element_access/get_neg.cc: Adjust.
    * testsuite/20_util/tuple/tuple_traits.cc: New.
-------------- next part --------------
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index c06a040..9f43732 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -220,8 +220,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       constexpr _Tuple_impl(const _Tuple_impl&) = default;
 
       constexpr
-      _Tuple_impl(_Tuple_impl&& __in)
-      noexcept(__and_<is_nothrow_move_constructible<_Head>,
+      _Tuple_impl(typename conditional<
+		  __and_<is_move_constructible<_Head>,
+		         is_move_constructible<_Inherited>>::value,
+		  _Tuple_impl&&, __nonesuch&&>::type __in)
+	noexcept(__and_<is_nothrow_move_constructible<_Head>,
 	              is_nothrow_move_constructible<_Inherited>>::value)
       : _Inherited(std::move(_M_tail(__in))),
 	_Base(std::forward<_Head>(_M_head(__in))) { }
@@ -232,7 +235,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _Base(_Tuple_impl<_Idx, _UElements...>::_M_head(__in)) { }
 
       template<typename _UHead, typename... _UTails>
-        constexpr _Tuple_impl(_Tuple_impl<_Idx, _UHead, _UTails...>&& __in)
+        constexpr _Tuple_impl(typename conditional<
+			      __and_<is_move_constructible<_Head>,
+			      is_move_constructible<_Inherited>>::value,
+			      _Tuple_impl<_Idx, _UHead, _UTails...>&&,
+			      __nonesuch&&>::type __in)
 	: _Inherited(std::move
 		     (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in))),
 	  _Base(std::forward<_UHead>
@@ -338,6 +345,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
     };
 
+  template<typename...>
+    struct __is_tuple_impl_trait_impl : false_type
+  { };
+
+  template<std::size_t _Idx, typename... _Tp>
+    struct __is_tuple_impl_trait_impl<_Tuple_impl<_Idx, _Tp...>> : true_type
+  { };
+
+  template<typename _Tp>
+    struct __is_tuple_impl_trait : public __is_tuple_impl_trait_impl<_Tp>
+  { };
+
   // Basis case of inheritance recursion.
   template<std::size_t _Idx, typename _Head>
     struct _Tuple_impl<_Idx, _Head>
@@ -356,11 +375,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       constexpr _Tuple_impl()
       : _Base() { }
 
+      template<typename _Dummy=void,
+	       typename enable_if<is_constructible<_Base, const _Head&>::value,
+				  bool>::type=true>
       explicit
       constexpr _Tuple_impl(const _Head& __head)
       : _Base(__head) { }
 
-      template<typename _UHead>
+      template<typename _UHead,
+	       typename enable_if<__and_<is_constructible<_Base, _UHead&&>,
+					 __not_<__is_tuple_impl_trait<
+					   typename
+					     remove_reference<_UHead>::type>>
+					 >::value,
+				  bool>::type = true>
         explicit
         constexpr _Tuple_impl(_UHead&& __head)
 	: _Base(std::forward<_UHead>(__head)) { }
@@ -368,15 +396,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       constexpr _Tuple_impl(const _Tuple_impl&) = default;
 
       constexpr
-      _Tuple_impl(_Tuple_impl&& __in)
+      _Tuple_impl(typename conditional<
+		  is_move_constructible<_Head>::value,
+		  _Tuple_impl&&, __nonesuch&&>::type __in)
       noexcept(is_nothrow_move_constructible<_Head>::value)
       : _Base(std::forward<_Head>(_M_head(__in))) { }
 
-      template<typename _UHead>
+      template<typename _UHead,
+	       typename enable_if<!is_same<_Head, _UHead>::value,
+				  bool>::type = true>
         constexpr _Tuple_impl(const _Tuple_impl<_Idx, _UHead>& __in)
 	: _Base(_Tuple_impl<_Idx, _UHead>::_M_head(__in)) { }
 
-      template<typename _UHead>
+      template<typename _UHead,
+	       typename enable_if<!is_same<_Head, _UHead>::value,
+				  bool>::type = true>
         constexpr _Tuple_impl(_Tuple_impl<_Idx, _UHead>&& __in)
 	: _Base(std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
 	{ }
@@ -832,14 +866,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{ }
 
       tuple&
-      operator=(const tuple& __in)
+      operator=(typename
+		conditional<__and_<is_copy_assignable<_Elements>...>::value,
+		const tuple&, const __nonesuch&>::type __in)
       {
 	static_cast<_Inherited&>(*this) = __in;
 	return *this;
       }
 
       tuple&
-      operator=(tuple&& __in)
+      operator=(typename
+		conditional<__and_<is_move_assignable<_Elements>...>::value,
+		tuple&&, __nonesuch&&>::type __in)
       noexcept(is_nothrow_move_assignable<_Inherited>::value)
       {
 	static_cast<_Inherited&>(*this) = std::move(__in);
@@ -848,7 +886,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename... _UElements, typename = typename
 	       enable_if<sizeof...(_UElements)
-			 == sizeof...(_Elements)>::type>
+			 == sizeof...(_Elements)
+	       &&
+	       __and_<is_assignable<_Elements&,
+				    const _UElements&>...>::value>::type>
         tuple&
         operator=(const tuple<_UElements...>& __in)
         {
@@ -858,7 +899,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename... _UElements, typename = typename
 	       enable_if<sizeof...(_UElements)
-			 == sizeof...(_Elements)>::type>
+			 == sizeof...(_Elements)
+	       &&
+	       __and_<is_assignable<_Elements&,
+				    _UElements&&>...>::value>::type>
         tuple&
         operator=(tuple<_UElements...>&& __in)
         {
@@ -1189,14 +1233,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		     std::forward<_U2>(__in.second)) { }
 
       tuple&
-      operator=(const tuple& __in)
+      operator=(typename
+		conditional<__and_<is_copy_assignable<_T1>,
+		                   is_copy_assignable<_T2>>::value,
+		const tuple&, const __nonesuch&>::type __in)
       {
 	static_cast<_Inherited&>(*this) = __in;
 	return *this;
       }
 
       tuple&
-      operator=(tuple&& __in)
+      operator=(typename
+		conditional<__and_<is_move_assignable<_T1>,
+		                   is_move_assignable<_T2>>::value,
+		tuple&&, __nonesuch&&>::type __in)
       noexcept(is_nothrow_move_assignable<_Inherited>::value)
       {
 	static_cast<_Inherited&>(*this) = std::move(__in);
@@ -1204,7 +1254,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       template<typename _U1, typename _U2>
-        tuple&
+        typename
+          enable_if<__and_<is_assignable<_T1&, const _U1&>,
+			   is_assignable<_T2&, const _U2&>>::value,
+			   tuple&>::type
         operator=(const tuple<_U1, _U2>& __in)
         {
 	  static_cast<_Inherited&>(*this) = __in;
@@ -1212,7 +1265,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
       template<typename _U1, typename _U2>
-        tuple&
+        typename
+          enable_if<__and_<is_assignable<_T1&, _U1&&>,
+			   is_assignable<_T2&, _U2&&>>::value,
+			   tuple&>::type
         operator=(tuple<_U1, _U2>&& __in)
         {
 	  static_cast<_Inherited&>(*this) = std::move(__in);
@@ -1220,7 +1276,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
       template<typename _U1, typename _U2>
-        tuple&
+        typename
+          enable_if<__and_<is_assignable<_T1&, const _U1&>,
+			   is_assignable<_T2&, const _U2&>>::value,
+			   tuple&>::type
         operator=(const pair<_U1, _U2>& __in)
         {
 	  this->_M_head(*this) = __in.first;
@@ -1229,7 +1288,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
       template<typename _U1, typename _U2>
-        tuple&
+        typename
+          enable_if<__and_<is_assignable<_T1&, _U1&&>,
+			   is_assignable<_T2&, _U2&&>>::value,
+			   tuple&>::type
         operator=(pair<_U1, _U2>&& __in)
         {
 	  this->_M_head(*this) = std::forward<_U1>(__in.first);
diff --git a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
index 5bcf576..1c08d45 100644
--- a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
@@ -17,7 +17,7 @@
 
 // { dg-options "-fno-show-column" }
 // { dg-do compile { target c++14 } }
-// { dg-error "in range" "" { target *-*-* } 1280 }
+// { dg-error "in range" "" { target *-*-* } 1342 }
 
 #include <tuple>
 
diff --git a/libstdc++-v3/testsuite/20_util/tuple/tuple_traits.cc b/libstdc++-v3/testsuite/20_util/tuple/tuple_traits.cc
new file mode 100644
index 0000000..b4dde99
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/tuple_traits.cc
@@ -0,0 +1,50 @@
+// { dg-do compile { target c++11 } }
+
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+using namespace std;
+
+struct Poison
+{
+	Poison(Poison&&) = delete;
+};
+
+
+int main()
+{
+	static_assert(!is_copy_constructible<Poison>::value, "");
+	static_assert(!is_move_constructible<Poison>::value, "");
+	static_assert(!is_copy_assignable<Poison>::value, "");
+	static_assert(!is_move_assignable<Poison>::value, "");
+	static_assert(!is_copy_constructible<std::tuple<int, Poison>>::value, "");
+	static_assert(!is_move_constructible<std::tuple<int, Poison>>::value, "");
+	static_assert(!is_copy_assignable<std::tuple<int, Poison>>::value, "");
+	static_assert(!is_move_assignable<std::tuple<int, Poison>>::value, "");
+	static_assert(!is_constructible<std::tuple<int, Poison>&, std::tuple<char, Poison>&>::value, "");
+	static_assert(!is_assignable<std::tuple<int, Poison>&, std::tuple<char, Poison>&>::value, "");
+	static_assert(!is_constructible<std::tuple<int, Poison>&, std::tuple<char, Poison>>::value, "");
+	static_assert(!is_assignable<std::tuple<int, Poison>&, std::tuple<char, Poison>>::value, "");
+	static_assert(!is_constructible<std::tuple<int, Poison>&, std::pair<char, Poison>&>::value, "");
+	static_assert(!is_assignable<std::tuple<int, Poison>&, std::pair<char, Poison>&>::value, "");
+	static_assert(!is_constructible<std::tuple<int, Poison>&, std::pair<char, Poison>>::value, "");
+	static_assert(!is_assignable<std::tuple<int, Poison>&, std::pair<char, Poison>>::value, "");
+}


More information about the Gcc-patches mailing list