Patchwork fix libstdc++/55463 calling mem_fn with rvalues

login
register
mail settings
Submitter Jonathan Wakely
Date Nov. 26, 2012, 11:51 p.m.
Message ID <CAH6eHdSKEvhq2UQe21nwwibU-zocHUHz_99UYuuqA5OienyXqg@mail.gmail.com>
Download mbox | patch
Permalink /patch/202045/
State New
Headers show

Comments

Jonathan Wakely - Nov. 26, 2012, 11:51 p.m.
PR libstdc++/55463
        * include/std/functional (_Mem_fn): Handle rvalue objects. Add
        noexcept-specifications.
        * testsuite/20_util/function_objects/mem_fn/55463.cc: New.
        * testsuite/20_util/bind/ref_neg.cc: Adjust dg-error line numbers.

Tested x86_64-linux, committed to trunk.
commit 4ddc9752bc93b233efcff9273b0fe4e488a783b8
Author: Jonathan Wakely <jwakely.gcc@gmail.com>
Date:   Sun Nov 25 21:56:23 2012 +0000

    	PR libstdc++/55463
    	* include/std/functional (_Mem_fn): Handle rvalue objects. Add
    	noexcept-specifications.
    	* testsuite/20_util/function_objects/mem_fn/55463.cc: New.
    	* testsuite/20_util/bind/ref_neg.cc: Adjust dg-error line numbers.

Patch

diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 4d6e6a8..1a98127 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -65,7 +65,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     class _Mem_fn;
   template<typename _Tp, typename _Class>
     _Mem_fn<_Tp _Class::*>
-    mem_fn(_Tp _Class::*);
+    mem_fn(_Tp _Class::*) noexcept;
 
 _GLIBCXX_HAS_NESTED_TYPE(result_type)
 
@@ -528,13 +528,16 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 
       template<typename _Tp>
 	_Res
-	_M_call(_Tp& __object, const volatile _Class *,
+	_M_call(_Tp&& __object, const volatile _Class *,
 		_ArgTypes... __args) const
-	{ return (__object.*__pmf)(std::forward<_ArgTypes>(__args)...); }
+	{
+	  return (std::forward<_Tp>(__object).*__pmf)
+	    (std::forward<_ArgTypes>(__args)...);
+	}
 
       template<typename _Tp>
 	_Res
-	_M_call(_Tp& __ptr, const volatile void *, _ArgTypes... __args) const
+	_M_call(_Tp&& __ptr, const volatile void *, _ArgTypes... __args) const
 	{ return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
 
     public:
@@ -555,12 +558,17 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
       // Handle smart pointers, references and pointers to derived
       template<typename _Tp>
 	_Res
-	operator()(_Tp& __object, _ArgTypes... __args) const
+	operator()(_Tp&& __object, _ArgTypes... __args) const
 	{
-	  return _M_call(__object, &__object,
+	  return _M_call(std::forward<_Tp>(__object), &__object,
 	      std::forward<_ArgTypes>(__args)...);
 	}
 
+      template<typename _Tp>
+	_Res
+	operator()(reference_wrapper<_Tp> __ref, _ArgTypes... __args) const
+	{ return operator()(__ref.get(), std::forward<_ArgTypes>(__args)...); }
+
     private:
       _Functor __pmf;
     };
@@ -575,13 +583,16 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 
       template<typename _Tp>
 	_Res
-	_M_call(_Tp& __object, const volatile _Class *,
+	_M_call(_Tp&& __object, const volatile _Class *,
 		_ArgTypes... __args) const
-	{ return (__object.*__pmf)(std::forward<_ArgTypes>(__args)...); }
+	{
+	  return (std::forward<_Tp>(__object).*__pmf)
+	    (std::forward<_ArgTypes>(__args)...);
+	}
 
       template<typename _Tp>
 	_Res
-	_M_call(_Tp& __ptr, const volatile void *, _ArgTypes... __args) const
+	_M_call(_Tp&& __ptr, const volatile void *, _ArgTypes... __args) const
 	{ return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
 
     public:
@@ -601,12 +612,17 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 
       // Handle smart pointers, references and pointers to derived
       template<typename _Tp>
-	_Res operator()(_Tp& __object, _ArgTypes... __args) const
+	_Res operator()(_Tp&& __object, _ArgTypes... __args) const
 	{
-	  return _M_call(__object, &__object,
+	  return _M_call(std::forward<_Tp>(__object), &__object,
 	      std::forward<_ArgTypes>(__args)...);
 	}
 
+      template<typename _Tp>
+	_Res
+	operator()(reference_wrapper<_Tp> __ref, _ArgTypes... __args) const
+	{ return operator()(__ref.get(), std::forward<_ArgTypes>(__args)...); }
+
     private:
       _Functor __pmf;
     };
@@ -621,13 +637,16 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 
       template<typename _Tp>
 	_Res
-	_M_call(_Tp& __object, const volatile _Class *,
+	_M_call(_Tp&& __object, const volatile _Class *,
 		_ArgTypes... __args) const
-	{ return (__object.*__pmf)(std::forward<_ArgTypes>(__args)...); }
+	{
+	  return (std::forward<_Tp>(__object).*__pmf)
+	    (std::forward<_ArgTypes>(__args)...);
+	}
 
       template<typename _Tp>
 	_Res
-	_M_call(_Tp& __ptr, const volatile void *, _ArgTypes... __args) const
+	_M_call(_Tp&& __ptr, const volatile void *, _ArgTypes... __args) const
 	{ return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
 
     public:
@@ -648,12 +667,17 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
       // Handle smart pointers, references and pointers to derived
       template<typename _Tp>
 	_Res
-	operator()(_Tp& __object, _ArgTypes... __args) const
+	operator()(_Tp&& __object, _ArgTypes... __args) const
 	{
-	  return _M_call(__object, &__object,
+	  return _M_call(std::forward<_Tp>(__object), &__object,
 	      std::forward<_ArgTypes>(__args)...);
 	}
 
+      template<typename _Tp>
+	_Res
+	operator()(reference_wrapper<_Tp> __ref, _ArgTypes... __args) const
+	{ return operator()(__ref.get(), std::forward<_ArgTypes>(__args)...); }
+
     private:
       _Functor __pmf;
     };
@@ -668,13 +692,16 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 
       template<typename _Tp>
 	_Res
-	_M_call(_Tp& __object, const volatile _Class *,
+	_M_call(_Tp&& __object, const volatile _Class *,
 		_ArgTypes... __args) const
-	{ return (__object.*__pmf)(std::forward<_ArgTypes>(__args)...); }
+	{
+	  return (std::forward<_Tp>(__object).*__pmf)
+	    (std::forward<_ArgTypes>(__args)...);
+	}
 
       template<typename _Tp>
 	_Res
-	_M_call(_Tp& __ptr, const volatile void *, _ArgTypes... __args) const
+	_M_call(_Tp&& __ptr, const volatile void *, _ArgTypes... __args) const
 	{ return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
 
     public:
@@ -694,12 +721,17 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 
       // Handle smart pointers, references and pointers to derived
       template<typename _Tp>
-	_Res operator()(_Tp& __object, _ArgTypes... __args) const
+	_Res operator()(_Tp&& __object, _ArgTypes... __args) const
 	{
-	  return _M_call(__object, &__object,
+	  return _M_call(std::forward<_Tp>(__object), &__object,
 	      std::forward<_ArgTypes>(__args)...);
 	}
 
+      template<typename _Tp>
+	_Res
+	operator()(reference_wrapper<_Tp> __ref, _ArgTypes... __args) const
+	{ return operator()(__ref.get(), std::forward<_ArgTypes>(__args)...); }
+
     private:
       _Functor __pmf;
     };
@@ -720,91 +752,75 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
   template<typename _Res, typename _Class>
     class _Mem_fn<_Res _Class::*>
     {
+      using __pm_type = _Res _Class::*;
+
       // This bit of genius is due to Peter Dimov, improved slightly by
       // Douglas Gregor.
+      // Made less elegant by Jonathan Wakely to support perfect forwarding.
       template<typename _Tp>
-	_Res&
-	_M_call(_Tp& __object, _Class *) const
-	{ return __object.*__pm; }
-
-      template<typename _Tp, typename _Up>
-	_Res&
-	_M_call(_Tp& __object, _Up * const *) const
-	{ return (*__object).*__pm; }
+	auto
+	_M_call(_Tp&& __object, const _Class *) const noexcept
+	-> decltype(std::forward<_Tp>(__object).*std::declval<__pm_type&>())
+	{ return std::forward<_Tp>(__object).*__pm; }
 
       template<typename _Tp, typename _Up>
-	const _Res&
-	_M_call(_Tp& __object, const _Up * const *) const
-	{ return (*__object).*__pm; }
-
-      template<typename _Tp>
-	const _Res&
-	_M_call(_Tp& __object, const _Class *) const
-	{ return __object.*__pm; }
+	auto
+	_M_call(_Tp&& __object, _Up * const *) const noexcept
+	-> decltype((*std::forward<_Tp>(__object)).*std::declval<__pm_type&>())
+	{ return (*std::forward<_Tp>(__object)).*__pm; }
 
       template<typename _Tp>
-	const _Res&
-	_M_call(_Tp& __ptr, const volatile void*) const
+	auto
+	_M_call(_Tp&& __ptr, const volatile void*) const
+	noexcept(noexcept((*__ptr).*std::declval<__pm_type&>()))
+	-> decltype((*__ptr).*std::declval<__pm_type&>())
 	{ return (*__ptr).*__pm; }
 
-      template<typename _Tp> static _Tp& __get_ref();
-
-      template<typename _Tp>
-	static __sfinae_types::__one __check_const(_Tp&, _Class*);
-      template<typename _Tp, typename _Up>
-	static __sfinae_types::__one __check_const(_Tp&, _Up * const *);
-      template<typename _Tp, typename _Up>
-	static __sfinae_types::__two __check_const(_Tp&, const _Up * const *);
-      template<typename _Tp>
-	static __sfinae_types::__two __check_const(_Tp&, const _Class*);
-      template<typename _Tp>
-	static __sfinae_types::__two __check_const(_Tp&, const volatile void*);
-
     public:
-      template<typename _Tp>
-	struct _Result_type
-	: _Mem_fn_const_or_non<_Res,
-	  (sizeof(__sfinae_types::__two)
-	   == sizeof(__check_const<_Tp>(__get_ref<_Tp>(), (_Tp*)0)))>
-	{ };
-
-      template<typename _Signature>
-	struct result;
-
-      template<typename _CVMem, typename _Tp>
-	struct result<_CVMem(_Tp)>
-	: public _Result_type<_Tp> { };
-
-      template<typename _CVMem, typename _Tp>
-	struct result<_CVMem(_Tp&)>
-	: public _Result_type<_Tp> { };
-
       explicit
-      _Mem_fn(_Res _Class::*__pm) : __pm(__pm) { }
+      _Mem_fn(_Res _Class::*__pm) noexcept : __pm(__pm) { }
 
       // Handle objects
       _Res&
-      operator()(_Class& __object) const
+      operator()(_Class& __object) const noexcept
       { return __object.*__pm; }
 
       const _Res&
-      operator()(const _Class& __object) const
+      operator()(const _Class& __object) const noexcept
       { return __object.*__pm; }
 
+      _Res&&
+      operator()(_Class&& __object) const noexcept
+      { return std::forward<_Class>(__object).*__pm; }
+
+      const _Res&&
+      operator()(const _Class&& __object) const noexcept
+      { return std::forward<const _Class>(__object).*__pm; }
+
       // Handle pointers
       _Res&
-      operator()(_Class* __object) const
+      operator()(_Class* __object) const noexcept
       { return __object->*__pm; }
 
       const _Res&
-      operator()(const _Class* __object) const
+      operator()(const _Class* __object) const noexcept
       { return __object->*__pm; }
 
       // Handle smart pointers and derived
       template<typename _Tp>
-	typename _Result_type<_Tp>::type
-	operator()(_Tp& __unknown) const
-	{ return _M_call(__unknown, &__unknown); }
+	auto
+	operator()(_Tp&& __unknown) const
+	noexcept(noexcept(std::declval<_Mem_fn*>()->_M_call
+			  (std::forward<_Tp>(__unknown), &__unknown)))
+	-> decltype(this->_M_call(std::forward<_Tp>(__unknown), &__unknown))
+	{ return _M_call(std::forward<_Tp>(__unknown), &__unknown); }
+
+      template<typename _Tp>
+	auto
+	operator()(reference_wrapper<_Tp> __ref) const
+	noexcept(noexcept(std::declval<_Mem_fn&>()(__ref.get())))
+	-> decltype((*this)(__ref.get()))
+	{ return operator()(__ref.get()); }
 
     private:
       _Res _Class::*__pm;
@@ -819,7 +835,7 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
    */
   template<typename _Tp, typename _Class>
     inline _Mem_fn<_Tp _Class::*>
-    mem_fn(_Tp _Class::* __pm)
+    mem_fn(_Tp _Class::* __pm) noexcept
     {
       return _Mem_fn<_Tp _Class::*>(__pm);
     }
diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
index 0f1b4cf..c7f605e 100644
--- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
@@ -30,10 +30,10 @@  void test01()
 {
   const int dummy = 0;
   std::bind(&inc, _1)(0);               // { dg-error  "no match" }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1208 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1222 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1236 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1250 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1224 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1238 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1252 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1266 }
   std::bind(&inc, std::ref(dummy))();	// { dg-error  "no match" }
 }
 
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/55463.cc b/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/55463.cc
new file mode 100644
index 0000000..5adce1b
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/55463.cc
@@ -0,0 +1,78 @@ 
+// { dg-options "-std=gnu++11" }
+// { dg-do compile }
+
+// Copyright (C) 2012 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/>.
+
+// PR libstdc++/55463 Passing rvalue objects to std::mem_fn
+
+#include <functional>
+
+struct X
+{
+  int& func();
+  char& func_c() const;
+  short& func_v() volatile;
+  double& func_cv() const volatile;
+
+  int data;
+};
+
+using CX = const X;
+
+struct smart_ptr
+{
+  X& operator*() const;
+};
+
+std::reference_wrapper<X> ref();
+std::reference_wrapper<const X> cref();
+
+void test01()
+{
+  int& i1 = std::mem_fn( &X::func )( X() );
+  int& i2 = std::mem_fn( &X::func )( smart_ptr() );
+  int& i3 = std::mem_fn( &X::func )( ref() );
+
+  char& c1 = std::mem_fn( &X::func_c )( X() );
+  char& c2 = std::mem_fn( &X::func_c )( CX() );
+  char& c3 = std::mem_fn( &X::func_c )( smart_ptr() );
+  char& c4 = std::mem_fn( &X::func_c )( ref() );
+  char& c5 = std::mem_fn( &X::func_c )( cref() );
+
+  short& s1 = std::mem_fn( &X::func_v )( X() );
+  short& s2 = std::mem_fn( &X::func_v )( smart_ptr() );
+  short& s3 = std::mem_fn( &X::func_v )( ref() );
+
+  double& d1 = std::mem_fn( &X::func_cv )( X() );
+  double& d2 = std::mem_fn( &X::func_cv )( CX() );
+  double& d3 = std::mem_fn( &X::func_cv )( smart_ptr() );
+  double& d4 = std::mem_fn( &X::func_cv )( ref() );
+  double& d5 = std::mem_fn( &X::func_cv )( cref() );
+
+  // [expr.mptr.oper]
+  // The result of a .* expression whose second operand is a pointer to a
+  // data member is of the same value category (3.10) as its first operand.
+  int&& rval = std::mem_fn( &X::data )( X() );
+  const int&& crval = std::mem_fn( &X::data )( CX() );
+
+  int& sval = std::mem_fn( &X::data )( smart_ptr() );
+
+  int& val = std::mem_fn( &X::data )( ref() );
+  const int& cval = std::mem_fn( &X::data )( cref() );
+}
+