Patchwork fix libstdc++/55463 calling mem_fn with rvalues

login
register
mail settings
Submitter Jonathan Wakely
Date Nov. 28, 2012, 1:42 a.m.
Message ID <CAH6eHdRxqsnC28WwP2SQwZnt0YJxwkjpap=B6sFSnocF1pwYnQ@mail.gmail.com>
Download mbox | patch
Permalink /patch/202351/
State New
Headers show

Comments

Jonathan Wakely - Nov. 28, 2012, 1:42 a.m.
On 26 November 2012 23:51, Jonathan Wakely wrote:
>         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.

That patch had some problems, fixed by this patch.  This also makes
the _Mem_fn call wrapper use perfect forwarding, avoiding an extra
copy when calling a member function that takes its arguments by value.

        * include/std/functional (_Mem_fn): Constrain function call operators
        to avoid ambiguities. Use perfect forwarding.
        * testsuite/20_util/function_objects/mem_fn/55463.cc: Additional
        tests.
        * testsuite/20_util/function_objects/mem_fn/forward.cc: New.
        * testsuite/20_util/bind/ref_neg.cc: Adjust dg-error line numbers.

Tested x86_64-linux, committed to trunk.


P.S. I have a major refactoring of mem_fn ready for when G++ supports
ref-qualifiers on member functions (tested with clang++) which avoids
having to repeat the current implementation three times for
&-qualified and &&-qualified member functions.  I might commit that
soon anyway because it already makes the existing code shorter, and
makes it trivial to add ref-qualifier support.
commit 959e0cd7f8c437048add0979416e35d84345c673
Author: Jonathan Wakely <jwakely.gcc@gmail.com>
Date:   Wed Nov 28 01:32:23 2012 +0000

    	* include/std/functional (_Mem_fn): Constrain function call operators
    	to avoid ambiguities. Use perfect forwarding.
    	* testsuite/20_util/function_objects/mem_fn/55463.cc: Additional
    	tests.
    	* testsuite/20_util/function_objects/mem_fn/forward.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 0d8fbd6..604481b 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -501,6 +501,26 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 
   // @} group functors
 
+  template<typename... _Cond>
+    using _Require = typename enable_if<__and_<_Cond...>::value>::type;
+
+  template<typename... _Types>
+    struct _Pack : integral_constant<size_t, sizeof...(_Types)>
+    { };
+
+  template<typename _From, typename _To, bool = _From::value == _To::value>
+    struct _AllConvertible : false_type
+    { };
+
+  template<typename... _From, typename... _To>
+    struct _AllConvertible<_Pack<_From...>, _Pack<_To...>, true>
+    : __and_<is_convertible<_From, _To>...>
+    { };
+
+  template<typename _Tp1, typename _Tp2>
+    using _NotSame = __not_<is_same<typename std::decay<_Tp1>::type,
+				    typename std::decay<_Tp2>::type>>;
+
   /**
    * Derives from @c unary_function or @c binary_function, or perhaps
    * nothing, depending on the number of arguments provided. The
@@ -526,19 +546,38 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
     {
       typedef _Res (_Class::*_Functor)(_ArgTypes...);
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args>
 	_Res
 	_M_call(_Tp&& __object, const volatile _Class *,
-		_ArgTypes... __args) const
+		_Args&&... __args) const
 	{
 	  return (std::forward<_Tp>(__object).*__pmf)
-	    (std::forward<_ArgTypes>(__args)...);
+	    (std::forward<_Args>(__args)...);
 	}
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args>
 	_Res
-	_M_call(_Tp&& __ptr, const volatile void *, _ArgTypes... __args) const
-	{ return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
+	_M_call(_Tp&& __ptr, const volatile void *, _Args&&... __args) const
+	{ return ((*__ptr).*__pmf)(std::forward<_Args>(__args)...); }
+
+      // Require each _Args to be convertible to corresponding _ArgTypes
+      template<typename... _Args>
+	using _RequireValidArgs
+	  = _Require<_AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
+
+      // Require each _Args to be convertible to corresponding _ArgTypes
+      // and require _Tp is not _Class, _Class& or _Class*
+      template<typename _Tp, typename... _Args>
+	using _RequireValidArgs2
+	  = _Require<_NotSame<_Class, _Tp>, _NotSame<_Class*, _Tp>,
+		     _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
+
+      // Require each _Args to be convertible to corresponding _ArgTypes
+      // and require _Tp is _Class or derived from _Class
+      template<typename _Tp, typename... _Args>
+	using _RequireValidArgs3
+	  = _Require<is_base_of<_Class, _Tp>,
+		     _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
 
     public:
       typedef _Res result_type;
@@ -546,28 +585,39 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
       explicit _Mem_fn(_Functor __pmf) : __pmf(__pmf) { }
 
       // Handle objects
-      _Res
-      operator()(_Class& __object, _ArgTypes... __args) const
-      { return (__object.*__pmf)(std::forward<_ArgTypes>(__args)...); }
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(_Class& __object, _Args&&... __args) const
+	{ return (__object.*__pmf)(std::forward<_Args>(__args)...); }
+
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(_Class&& __object, _Args&&... __args) const
+	{
+	  return (std::move(__object).*__pmf)(std::forward<_Args>(__args)...);
+	}
 
       // Handle pointers
-      _Res
-      operator()(_Class* __object, _ArgTypes... __args) const
-      { return (__object->*__pmf)(std::forward<_ArgTypes>(__args)...); }
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(_Class* __object, _Args&&... __args) const
+	{ return (__object->*__pmf)(std::forward<_Args>(__args)...); }
 
       // Handle smart pointers, references and pointers to derived
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args,
+	       typename _Req = _RequireValidArgs2<_Tp, _Args...>>
 	_Res
-	operator()(_Tp&& __object, _ArgTypes... __args) const
+	operator()(_Tp&& __object, _Args&&... __args) const
 	{
 	  return _M_call(std::forward<_Tp>(__object), &__object,
-	      std::forward<_ArgTypes>(__args)...);
+	      std::forward<_Args>(__args)...);
 	}
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args,
+	       typename _Req = _RequireValidArgs3<_Tp, _Args...>>
 	_Res
-	operator()(reference_wrapper<_Tp> __ref, _ArgTypes... __args) const
-	{ return operator()(__ref.get(), std::forward<_ArgTypes>(__args)...); }
+	operator()(reference_wrapper<_Tp> __ref, _Args&&... __args) const
+	{ return operator()(__ref.get(), std::forward<_Args>(__args)...); }
 
     private:
       _Functor __pmf;
@@ -581,19 +631,33 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
     {
       typedef _Res (_Class::*_Functor)(_ArgTypes...) const;
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args>
 	_Res
 	_M_call(_Tp&& __object, const volatile _Class *,
-		_ArgTypes... __args) const
+		_Args&&... __args) const
 	{
 	  return (std::forward<_Tp>(__object).*__pmf)
-	    (std::forward<_ArgTypes>(__args)...);
+	    (std::forward<_Args>(__args)...);
 	}
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args>
 	_Res
-	_M_call(_Tp&& __ptr, const volatile void *, _ArgTypes... __args) const
-	{ return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
+	_M_call(_Tp&& __ptr, const volatile void *, _Args&&... __args) const
+	{ return ((*__ptr).*__pmf)(std::forward<_Args>(__args)...); }
+
+      template<typename... _Args>
+	using _RequireValidArgs
+	  = _Require<_AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
+
+      template<typename _Tp, typename... _Args>
+	using _RequireValidArgs2
+	  = _Require<_NotSame<_Class, _Tp>, _NotSame<_Class*, _Tp>,
+		     _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
+
+      template<typename _Tp, typename... _Args>
+	using _RequireValidArgs3
+	  = _Require<is_base_of<_Class, _Tp>,
+		     _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
 
     public:
       typedef _Res result_type;
@@ -601,27 +665,38 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
       explicit _Mem_fn(_Functor __pmf) : __pmf(__pmf) { }
 
       // Handle objects
-      _Res
-      operator()(const _Class& __object, _ArgTypes... __args) const
-      { return (__object.*__pmf)(std::forward<_ArgTypes>(__args)...); }
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(const _Class& __object, _Args&&... __args) const
+	{ return (__object.*__pmf)(std::forward<_Args>(__args)...); }
+
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(const _Class&& __object, _Args&&... __args) const
+	{
+	  return (std::move(__object).*__pmf)(std::forward<_Args>(__args)...);
+	}
 
       // Handle pointers
-      _Res
-      operator()(const _Class* __object, _ArgTypes... __args) const
-      { return (__object->*__pmf)(std::forward<_ArgTypes>(__args)...); }
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(const _Class* __object, _Args&&... __args) const
+	{ return (__object->*__pmf)(std::forward<_Args>(__args)...); }
 
       // Handle smart pointers, references and pointers to derived
-      template<typename _Tp>
-	_Res operator()(_Tp&& __object, _ArgTypes... __args) const
+      template<typename _Tp, typename... _Args,
+	       typename _Req = _RequireValidArgs2<_Tp, _Args...>>
+	_Res operator()(_Tp&& __object, _Args&&... __args) const
 	{
 	  return _M_call(std::forward<_Tp>(__object), &__object,
-	      std::forward<_ArgTypes>(__args)...);
+	      std::forward<_Args>(__args)...);
 	}
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args,
+	       typename _Req = _RequireValidArgs3<_Tp, _Args...>>
 	_Res
-	operator()(reference_wrapper<_Tp> __ref, _ArgTypes... __args) const
-	{ return operator()(__ref.get(), std::forward<_ArgTypes>(__args)...); }
+	operator()(reference_wrapper<_Tp> __ref, _Args&&... __args) const
+	{ return operator()(__ref.get(), std::forward<_Args>(__args)...); }
 
     private:
       _Functor __pmf;
@@ -635,19 +710,33 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
     {
       typedef _Res (_Class::*_Functor)(_ArgTypes...) volatile;
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args>
 	_Res
 	_M_call(_Tp&& __object, const volatile _Class *,
-		_ArgTypes... __args) const
+		_Args&&... __args) const
 	{
 	  return (std::forward<_Tp>(__object).*__pmf)
-	    (std::forward<_ArgTypes>(__args)...);
+	    (std::forward<_Args>(__args)...);
 	}
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args>
 	_Res
-	_M_call(_Tp&& __ptr, const volatile void *, _ArgTypes... __args) const
-	{ return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
+	_M_call(_Tp&& __ptr, const volatile void *, _Args&&... __args) const
+	{ return ((*__ptr).*__pmf)(std::forward<_Args>(__args)...); }
+
+      template<typename... _Args>
+	using _RequireValidArgs
+	  = _Require<_AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
+
+      template<typename _Tp, typename... _Args>
+	using _RequireValidArgs2
+	  = _Require<_NotSame<_Class, _Tp>, _NotSame<_Class*, _Tp>,
+		     _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
+
+      template<typename _Tp, typename... _Args>
+	using _RequireValidArgs3
+	  = _Require<is_base_of<_Class, _Tp>,
+		     _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
 
     public:
       typedef _Res result_type;
@@ -655,28 +744,39 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
       explicit _Mem_fn(_Functor __pmf) : __pmf(__pmf) { }
 
       // Handle objects
-      _Res
-      operator()(volatile _Class& __object, _ArgTypes... __args) const
-      { return (__object.*__pmf)(std::forward<_ArgTypes>(__args)...); }
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(volatile _Class& __object, _Args&&... __args) const
+	{ return (__object.*__pmf)(std::forward<_Args>(__args)...); }
+
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(volatile _Class&& __object, _Args&&... __args) const
+	{
+	  return (std::move(__object).*__pmf)(std::forward<_Args>(__args)...);
+	}
 
       // Handle pointers
-      _Res
-      operator()(volatile _Class* __object, _ArgTypes... __args) const
-      { return (__object->*__pmf)(std::forward<_ArgTypes>(__args)...); }
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(volatile _Class* __object, _Args&&... __args) const
+	{ return (__object->*__pmf)(std::forward<_Args>(__args)...); }
 
       // Handle smart pointers, references and pointers to derived
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args,
+	       typename _Req = _RequireValidArgs2<_Tp, _Args...>>
 	_Res
-	operator()(_Tp&& __object, _ArgTypes... __args) const
+	operator()(_Tp&& __object, _Args&&... __args) const
 	{
 	  return _M_call(std::forward<_Tp>(__object), &__object,
-	      std::forward<_ArgTypes>(__args)...);
+	      std::forward<_Args>(__args)...);
 	}
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args,
+	       typename _Req = _RequireValidArgs3<_Tp, _Args...>>
 	_Res
-	operator()(reference_wrapper<_Tp> __ref, _ArgTypes... __args) const
-	{ return operator()(__ref.get(), std::forward<_ArgTypes>(__args)...); }
+	operator()(reference_wrapper<_Tp> __ref, _Args&&... __args) const
+	{ return operator()(__ref.get(), std::forward<_Args>(__args)...); }
 
     private:
       _Functor __pmf;
@@ -690,19 +790,33 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
     {
       typedef _Res (_Class::*_Functor)(_ArgTypes...) const volatile;
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args>
 	_Res
 	_M_call(_Tp&& __object, const volatile _Class *,
-		_ArgTypes... __args) const
+		_Args&&... __args) const
 	{
 	  return (std::forward<_Tp>(__object).*__pmf)
-	    (std::forward<_ArgTypes>(__args)...);
+	    (std::forward<_Args>(__args)...);
 	}
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args>
 	_Res
-	_M_call(_Tp&& __ptr, const volatile void *, _ArgTypes... __args) const
-	{ return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); }
+	_M_call(_Tp&& __ptr, const volatile void *, _Args&&... __args) const
+	{ return ((*__ptr).*__pmf)(std::forward<_Args>(__args)...); }
+
+      template<typename... _Args>
+	using _RequireValidArgs
+	  = _Require<_AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
+
+      template<typename _Tp, typename... _Args>
+	using _RequireValidArgs2
+	  = _Require<_NotSame<_Class, _Tp>, _NotSame<_Class*, _Tp>,
+		     _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
+
+      template<typename _Tp, typename... _Args>
+	using _RequireValidArgs3
+	  = _Require<is_base_of<_Class, _Tp>,
+		     _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;
 
     public:
       typedef _Res result_type;
@@ -710,27 +824,38 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
       explicit _Mem_fn(_Functor __pmf) : __pmf(__pmf) { }
 
       // Handle objects
-      _Res
-      operator()(const volatile _Class& __object, _ArgTypes... __args) const
-      { return (__object.*__pmf)(std::forward<_ArgTypes>(__args)...); }
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(const volatile _Class& __object, _Args&&... __args) const
+	{ return (__object.*__pmf)(std::forward<_Args>(__args)...); }
+
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(const volatile _Class&& __object, _Args&&... __args) const
+	{
+	  return (std::move(__object).*__pmf)(std::forward<_Args>(__args)...);
+	}
 
       // Handle pointers
-      _Res
-      operator()(const volatile _Class* __object, _ArgTypes... __args) const
-      { return (__object->*__pmf)(std::forward<_ArgTypes>(__args)...); }
+      template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
+	_Res
+	operator()(const volatile _Class* __object, _Args&&... __args) const
+	{ return (__object->*__pmf)(std::forward<_Args>(__args)...); }
 
       // Handle smart pointers, references and pointers to derived
-      template<typename _Tp>
-	_Res operator()(_Tp&& __object, _ArgTypes... __args) const
+      template<typename _Tp, typename... _Args,
+	       typename _Req = _RequireValidArgs2<_Tp, _Args...>>
+	_Res operator()(_Tp&& __object, _Args&&... __args) const
 	{
 	  return _M_call(std::forward<_Tp>(__object), &__object,
-	      std::forward<_ArgTypes>(__args)...);
+	      std::forward<_Args>(__args)...);
 	}
 
-      template<typename _Tp>
+      template<typename _Tp, typename... _Args,
+	       typename _Req = _RequireValidArgs3<_Tp, _Args...>>
 	_Res
-	operator()(reference_wrapper<_Tp> __ref, _ArgTypes... __args) const
-	{ return operator()(__ref.get(), std::forward<_ArgTypes>(__args)...); }
+	operator()(reference_wrapper<_Tp> __ref, _Args&&... __args) const
+	{ return operator()(__ref.get(), std::forward<_Args>(__args)...); }
 
     private:
       _Functor __pmf;
@@ -756,7 +881,7 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 
       // This bit of genius is due to Peter Dimov, improved slightly by
       // Douglas Gregor.
-      // Made less elegant by Jonathan Wakely to support perfect forwarding.
+      // Made less elegant to support perfect forwarding and noexcept.
       template<typename _Tp>
 	auto
 	_M_call(_Tp&& __object, const _Class *) const noexcept
@@ -807,7 +932,7 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
       { return __object->*__pm; }
 
       // Handle smart pointers and derived
-      template<typename _Tp>
+      template<typename _Tp, typename _Req = _Require<_NotSame<_Class*, _Tp>>>
 	auto
 	operator()(_Tp&& __unknown) const
 	noexcept(noexcept(std::declval<_Mem_fn*>()->_M_call
@@ -815,12 +940,12 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 	-> decltype(this->_M_call(std::forward<_Tp>(__unknown), &__unknown))
 	{ return _M_call(std::forward<_Tp>(__unknown), &__unknown); }
 
-      template<typename _Tp>
+      template<typename _Tp, typename _Req = _Require<is_base_of<_Class, _Tp>>>
 	auto
 	operator()(reference_wrapper<_Tp> __ref) const
 	noexcept(noexcept(std::declval<_Mem_fn&>()(__ref.get())))
 	-> decltype((*this)(__ref.get()))
-	{ return operator()(__ref.get()); }
+	{ return (*this)(__ref.get()); }
 
     private:
       _Res _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 c7f605e..bae0a86 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 *-*-* } 1224 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1238 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1252 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1266 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1349 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1363 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1377 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1391 }
   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
index 5adce1b..ac11852 100644
--- a/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/55463.cc
+++ b/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/55463.cc
@@ -32,7 +32,12 @@  struct X
   int data;
 };
 
+struct Y : X { };
+
 using CX = const X;
+using CY = const Y;
+
+using X_ptr = X*;
 
 struct smart_ptr
 {
@@ -41,38 +46,63 @@  struct smart_ptr
 
 std::reference_wrapper<X> ref();
 std::reference_wrapper<const X> cref();
+std::reference_wrapper<Y> yref();
 
 void test01()
 {
   int& i1 = std::mem_fn( &X::func )( X() );
-  int& i2 = std::mem_fn( &X::func )( smart_ptr() );
+  int& i2 = std::mem_fn( &X::func )( Y() );
   int& i3 = std::mem_fn( &X::func )( ref() );
+  int& i4 = std::mem_fn( &X::func )( yref() );
+  int& i5 = std::mem_fn( &X::func )( X_ptr() );
+  int& i6 = std::mem_fn( &X::func )( smart_ptr() );
 
   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& c3 = std::mem_fn( &X::func_c )( Y() );
   char& c4 = std::mem_fn( &X::func_c )( ref() );
   char& c5 = std::mem_fn( &X::func_c )( cref() );
+  char& c6 = std::mem_fn( &X::func_c )( yref() );
+  char& c7 = std::mem_fn( &X::func_c )( X_ptr() );
+  char& c8 = std::mem_fn( &X::func_c )( smart_ptr() );
 
   short& s1 = std::mem_fn( &X::func_v )( X() );
-  short& s2 = std::mem_fn( &X::func_v )( smart_ptr() );
+  short& s2 = std::mem_fn( &X::func_v )( Y() );
   short& s3 = std::mem_fn( &X::func_v )( ref() );
+  short& s4 = std::mem_fn( &X::func_v )( yref() );
+  short& s5 = std::mem_fn( &X::func_v )( X_ptr() );
+  short& s6 = std::mem_fn( &X::func_v )( smart_ptr() );
 
   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& d3 = std::mem_fn( &X::func_cv )( Y() );
   double& d4 = std::mem_fn( &X::func_cv )( ref() );
   double& d5 = std::mem_fn( &X::func_cv )( cref() );
+  double& d6 = std::mem_fn( &X::func_cv )( yref() );
+  double& d7 = std::mem_fn( &X::func_cv )( X_ptr() );
+  double& d8 = std::mem_fn( &X::func_cv )( smart_ptr() );
 
   // [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&& yrval = std::mem_fn( &X::data )( Y() );
+  const int&& ycrval = std::mem_fn( &X::data )( CY() );
 
   int& val = std::mem_fn( &X::data )( ref() );
   const int& cval = std::mem_fn( &X::data )( cref() );
+  int& yval = std::mem_fn( &X::data )( yref() );
+
+  int& pval = std::mem_fn( &X::data )( X_ptr() );
+  int& sval = std::mem_fn( &X::data )( smart_ptr() );
 }
 
+void test02()
+{
+  std::reference_wrapper<X> r = ref();
+  X& x1 = std::mem_fn( &std::reference_wrapper<X>::get )( r );
+  const std::reference_wrapper<X> cr = ref();
+  const X& x3 = std::mem_fn( &std::reference_wrapper<X>::get )( cr );
+  X& x2 = std::mem_fn( &std::reference_wrapper<X>::get )( ref() );
+}
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/forward.cc b/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/forward.cc
new file mode 100644
index 0000000..a231bf6
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/forward.cc
@@ -0,0 +1,62 @@ 
+// { dg-options "-std=gnu++11" }
+
+// 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/>.
+
+#include <functional>
+#include <testsuite_hooks.h>
+
+struct Counter
+{
+  Counter() = default;
+  Counter(const Counter&) { ++count; }
+
+  static int count;
+};
+
+int Counter::count = 0;
+
+struct X
+{
+  int func(Counter, int i) { return i; }
+  char func_c(Counter, char c) const { return c; }
+  short func_v(Counter, short s) volatile { return s; }
+  double func_cv(Counter, double d) const volatile { return d; }
+};
+
+void test01()
+{
+  Counter c;
+  X x;
+
+  std::mem_fn( &X::func )( x, c, 0 );
+  VERIFY( Counter::count == 1 );
+
+  std::mem_fn( &X::func_c )( x, c, 0 );
+  VERIFY( Counter::count == 2 );
+
+  std::mem_fn( &X::func_v )( x, c, 0 );
+  VERIFY( Counter::count == 3 );
+
+  std::mem_fn( &X::func_cv )( x, c, 0 );
+  VERIFY( Counter::count == 4 );
+}
+
+int main()
+{
+  test01();
+}