diff mbox

Make std::bind use std::invoke

Message ID 20161014190450.GF2922@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely Oct. 14, 2016, 7:04 p.m. UTC
I think this is the last place where we weren't using std::__invoke()
for something the standard defines in terms of INVOKE. This should
make all our call wrappers and functional stuff consistent in terms of
support for invoking a pointer-to-member, reference_wrapper etc.

This means we can remove _Maybe_wrap_member_pointer, because
std::__invoke does the right thing for member pointers.

I've also added the deprecated attribute to the volatile overloads,
corresponding to the new wording in C++17:

  "The cv-qualifiers cv of the call wrapper g, as specified below,
  shall be neither volatile nor const volatile."


	* include/std/functional (_Mu<A, false, true>, _Mu<A, true, false>):
	Simplify forwarding from tuple of references.
	(_Maybe_wrap_member_pointer): Remove.
	(_Bind::__call, _Bind::__call_c, _Bind::__call_v, _Bind::__call_c_v):
	Use std::__invoke.
	(_Bind::_Mu_type, _Bind::_Res_type_impl, _Bind::_Res_type)
	(_Bind::__dependent, _Bind::_Res_type_cv): New helpers to simplify
	return type deduction.
	(_Bind::operator(), _Bind::operator() const): Use new helpers.
	(_Bind::operator() volatile, _Bind::operator() const volatile):
	Likewise. Add deprecated attribute for C++17 mode.
	(_Bind_result::__call): Use std::__invoke.
	(_Bind_result::operator() volatile)
	(_Bind_result::operator() const volatile): Add deprecated attribute.
	(_Bind_helper::__maybe_type, _Bindres_helper::__maybe_type): Remove.
	(_Bind_helper, _Bindres_helper): Don't use _Maybe_wrap_member_pointer.
	(bind, bind<R>): Don't use __maybe_type.
	* src/c++11/compatibility-thread-c++0x.cc
	(_Maybe_wrap_member_pointer): Define here for compatibility symbols.
	* testsuite/20_util/bind/68912.cc: Don't test volatile-qualification
	in C++17 mode.
	* testsuite/20_util/bind/cv_quals.cc: Likewise.
	* testsuite/20_util/bind/cv_quals_2.cc: Likewise.

Tested x86_64-linux, -std=gnu++{14,17},  committed to trunk.
commit 0a21180fd9ac92e6fe756f1956fded4c8f3149f2
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Oct 13 01:40:13 2016 +0100

    Make std::bind use std::invoke
    
    	* include/std/functional (_Mu<A, false, true>, _Mu<A, true, false>):
    	Simplify forwarding from tuple of references.
    	(_Maybe_wrap_member_pointer): Remove.
    	(_Bind::__call, _Bind::__call_c, _Bind::__call_v, _Bind::__call_c_v):
    	Use std::__invoke.
    	(_Bind::_Mu_type, _Bind::_Res_type_impl, _Bind::_Res_type)
    	(_Bind::__dependent, _Bind::_Res_type_cv): New helpers to simplify
    	return type deduction.
    	(_Bind::operator(), _Bind::operator() const): Use new helpers.
    	(_Bind::operator() volatile, _Bind::operator() const volatile):
    	Likewise. Add deprecated attribute for C++17 mode.
    	(_Bind_result::__call): Use std::__invoke.
    	(_Bind_result::operator() volatile)
    	(_Bind_result::operator() const volatile): Add deprecated attribute.
    	(_Bind_helper::__maybe_type, _Bindres_helper::__maybe_type): Remove.
    	(_Bind_helper, _Bindres_helper): Don't use _Maybe_wrap_member_pointer.
    	(bind, bind<R>): Don't use __maybe_type.
    	* src/c++11/compatibility-thread-c++0x.cc
    	(_Maybe_wrap_member_pointer): Define here for compatibility symbols.
    	* testsuite/20_util/bind/68912.cc: Don't test volatile-qualification
    	in C++17 mode.
    	* testsuite/20_util/bind/cv_quals.cc: Likewise.
    	* testsuite/20_util/bind/cv_quals_2.cc: Likewise.
diff mbox

Patch

diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index d39b519..ad67a1d 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -742,7 +742,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	       const _Index_tuple<_Indexes...>&) const volatile
 	-> decltype(__arg(declval<_Args>()...))
 	{
-	  return __arg(std::forward<_Args>(std::get<_Indexes>(__tuple))...);
+	  return __arg(std::get<_Indexes>(std::move(__tuple))...);
 	}
     };
 
@@ -759,10 +759,8 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	_Safe_tuple_element_t<(is_placeholder<_Arg>::value - 1), _Tuple>&&
 	operator()(const volatile _Arg&, _Tuple& __tuple) const volatile
 	{
-	  using __type
-	    = __tuple_element_t<(is_placeholder<_Arg>::value - 1), _Tuple>;
-	  return std::forward<__type>(
-	      ::std::get<(is_placeholder<_Arg>::value - 1)>(__tuple));
+	  return
+	    ::std::get<(is_placeholder<_Arg>::value - 1)>(std::move(__tuple));
 	}
     };
 
@@ -781,50 +779,6 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	{ return std::forward<_CVArg>(__arg); }
     };
 
-  /**
-   *  Maps member pointers into instances of _Mem_fn but leaves all
-   *  other function objects untouched. Used by std::bind(). The
-   *  primary template handles the non-member-pointer case.
-   */
-  template<typename _Tp>
-    struct _Maybe_wrap_member_pointer
-    {
-      typedef _Tp type;
-
-      static constexpr const _Tp&
-      __do_wrap(const _Tp& __x)
-      { return __x; }
-
-      static constexpr _Tp&&
-      __do_wrap(_Tp&& __x)
-      { return static_cast<_Tp&&>(__x); }
-    };
-
-  /**
-   *  Maps member pointers into instances of _Mem_fn but leaves all
-   *  other function objects untouched. Used by std::bind(). This
-   *  partial specialization handles the member pointer case.
-   */
-  template<typename _Tp, typename _Class>
-    struct _Maybe_wrap_member_pointer<_Tp _Class::*>
-    {
-      typedef _Mem_fn<_Tp _Class::*> type;
-
-      static constexpr type
-      __do_wrap(_Tp _Class::* __pm)
-      { return type(__pm); }
-    };
-
-  // Specialization needed to prevent "forming reference to void" errors when
-  // bind<void>() is called, because argument deduction instantiates
-  // _Maybe_wrap_member_pointer<void> outside the immediate context where
-  // SFINAE applies.
-  template<>
-    struct _Maybe_wrap_member_pointer<void>
-    {
-      typedef void type;
-    };
-
   // std::get<I> for volatile-qualified tuples
   template<std::size_t _Ind, typename... _Tp>
     inline auto
@@ -858,8 +812,9 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	_Result
 	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>)
 	{
-	  return _M_f(_Mu<_Bound_args>()
-		      (std::get<_Indexes>(_M_bound_args), __args)...);
+	  return std::__invoke(_M_f,
+	      _Mu<_Bound_args>()(std::get<_Indexes>(_M_bound_args), __args)...
+	      );
 	}
 
       // Call as const
@@ -867,8 +822,9 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	_Result
 	__call_c(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>) const
 	{
-	  return _M_f(_Mu<_Bound_args>()
-		      (std::get<_Indexes>(_M_bound_args), __args)...);
+	  return std::__invoke(_M_f,
+	      _Mu<_Bound_args>()(std::get<_Indexes>(_M_bound_args), __args)...
+	      );
 	}
 
       // Call as volatile
@@ -877,8 +833,9 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	__call_v(tuple<_Args...>&& __args,
 		 _Index_tuple<_Indexes...>) volatile
 	{
-	  return _M_f(_Mu<_Bound_args>()
-		      (__volget<_Indexes>(_M_bound_args), __args)...);
+	  return std::__invoke(_M_f,
+	      _Mu<_Bound_args>()(__volget<_Indexes>(_M_bound_args), __args)...
+	      );
 	}
 
       // Call as const volatile
@@ -887,10 +844,33 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	__call_c_v(tuple<_Args...>&& __args,
 		   _Index_tuple<_Indexes...>) const volatile
 	{
-	  return _M_f(_Mu<_Bound_args>()
-		      (__volget<_Indexes>(_M_bound_args), __args)...);
+	  return std::__invoke(_M_f,
+	      _Mu<_Bound_args>()(__volget<_Indexes>(_M_bound_args), __args)...
+	      );
 	}
 
+      template<typename _BoundArg, typename _CallArgs>
+	using _Mu_type = decltype(
+	    _Mu<typename remove_cv<_BoundArg>::type>()(
+	      std::declval<_BoundArg&>(), std::declval<_CallArgs&>()) );
+
+      template<typename _Fn, typename _CallArgs, typename... _BArgs>
+	using _Res_type_impl
+	  = typename result_of< _Fn&(_Mu_type<_BArgs, _CallArgs>...) >::type;
+
+      template<typename _CallArgs>
+	using _Res_type = _Res_type_impl<_Functor, _CallArgs, _Bound_args...>;
+
+      template<typename _CallArgs>
+	using __dependent = typename
+	  enable_if<bool(tuple_size<_CallArgs>::value+1), _Functor>::type;
+
+      template<typename _CallArgs, template<class> class __cv_quals>
+	using _Res_type_cv = _Res_type_impl<
+	  typename __cv_quals<__dependent<_CallArgs>>::type,
+	  _CallArgs,
+	  typename __cv_quals<_Bound_args>::type...>;
+
      public:
       template<typename... _Args>
 	explicit _Bind(const _Functor& __f, _Args&&... __args)
@@ -909,10 +889,8 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
       { }
 
       // Call unqualified
-      template<typename... _Args, typename _Result
-	= decltype( std::declval<_Functor&>()(
-	      _Mu<_Bound_args>()( std::declval<_Bound_args&>(),
-				  std::declval<tuple<_Args...>&>() )... ) )>
+      template<typename... _Args,
+	       typename _Result = _Res_type<tuple<_Args...>>>
 	_Result
 	operator()(_Args&&... __args)
 	{
@@ -922,11 +900,8 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	}
 
       // Call as const
-      template<typename... _Args, typename _Result
-	= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
-		       typename add_const<_Functor>::type&>::type>()(
-	      _Mu<_Bound_args>()( std::declval<const _Bound_args&>(),
-				  std::declval<tuple<_Args...>&>() )... ) )>
+      template<typename... _Args,
+	       typename _Result = _Res_type_cv<tuple<_Args...>, add_const>>
 	_Result
 	operator()(_Args&&... __args) const
 	{
@@ -935,12 +910,16 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	      _Bound_indexes());
 	}
 
+#if __cplusplus > 201402L
+# define _GLIBCXX_DEPR_BIND \
+      [[deprecated("std::bind does not support volatile in C++17")]]
+#else
+# define _GLIBCXX_DEPR_BIND
+#endif
       // Call as volatile
-      template<typename... _Args, typename _Result
-	= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
-                       typename add_volatile<_Functor>::type&>::type>()(
-	      _Mu<_Bound_args>()( std::declval<volatile _Bound_args&>(),
-				  std::declval<tuple<_Args...>&>() )... ) )>
+      template<typename... _Args,
+	       typename _Result = _Res_type_cv<tuple<_Args...>, add_volatile>>
+	_GLIBCXX_DEPR_BIND
 	_Result
 	operator()(_Args&&... __args) volatile
 	{
@@ -950,11 +929,9 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	}
 
       // Call as const volatile
-      template<typename... _Args, typename _Result
-	= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
-                       typename add_cv<_Functor>::type&>::type>()(
-	      _Mu<_Bound_args>()( std::declval<const volatile _Bound_args&>(),
-				  std::declval<tuple<_Args...>&>() )... ) )>
+      template<typename... _Args,
+	       typename _Result = _Res_type_cv<tuple<_Args...>, add_cv>>
+	_GLIBCXX_DEPR_BIND
 	_Result
 	operator()(_Args&&... __args) const volatile
 	{
@@ -991,7 +968,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	__disable_if_void<_Res>
 	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>)
 	{
-	  return _M_f(_Mu<_Bound_args>()
+	  return std::__invoke(_M_f, _Mu<_Bound_args>()
 		      (std::get<_Indexes>(_M_bound_args), __args)...);
 	}
 
@@ -1000,7 +977,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	__enable_if_void<_Res>
 	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>)
 	{
-	  _M_f(_Mu<_Bound_args>()
+	  std::__invoke(_M_f, _Mu<_Bound_args>()
 	       (std::get<_Indexes>(_M_bound_args), __args)...);
 	}
 
@@ -1009,7 +986,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	__disable_if_void<_Res>
 	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>) const
 	{
-	  return _M_f(_Mu<_Bound_args>()
+	  return std::__invoke(_M_f, _Mu<_Bound_args>()
 		      (std::get<_Indexes>(_M_bound_args), __args)...);
 	}
 
@@ -1018,7 +995,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	__enable_if_void<_Res>
 	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>) const
 	{
-	  _M_f(_Mu<_Bound_args>()
+	  std::__invoke(_M_f, _Mu<_Bound_args>()
 	       (std::get<_Indexes>(_M_bound_args),  __args)...);
 	}
 
@@ -1027,7 +1004,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	__disable_if_void<_Res>
 	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>) volatile
 	{
-	  return _M_f(_Mu<_Bound_args>()
+	  return std::__invoke(_M_f, _Mu<_Bound_args>()
 		      (__volget<_Indexes>(_M_bound_args), __args)...);
 	}
 
@@ -1036,7 +1013,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	__enable_if_void<_Res>
 	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>) volatile
 	{
-	  _M_f(_Mu<_Bound_args>()
+	  std::__invoke(_M_f, _Mu<_Bound_args>()
 	       (__volget<_Indexes>(_M_bound_args), __args)...);
 	}
 
@@ -1046,7 +1023,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	__call(tuple<_Args...>&& __args,
 	       _Index_tuple<_Indexes...>) const volatile
 	{
-	  return _M_f(_Mu<_Bound_args>()
+	  return std::__invoke(_M_f, _Mu<_Bound_args>()
 		      (__volget<_Indexes>(_M_bound_args), __args)...);
 	}
 
@@ -1056,7 +1033,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	__call(tuple<_Args...>&& __args,
 	       _Index_tuple<_Indexes...>) const volatile
 	{
-	  _M_f(_Mu<_Bound_args>()
+	  std::__invoke(_M_f, _Mu<_Bound_args>()
 	       (__volget<_Indexes>(_M_bound_args), __args)...);
 	}
 
@@ -1101,6 +1078,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 
       // Call as volatile
       template<typename... _Args>
+	_GLIBCXX_DEPR_BIND
 	result_type
 	operator()(_Args&&... __args) volatile
 	{
@@ -1111,6 +1089,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 
       // Call as const volatile
       template<typename... _Args>
+	_GLIBCXX_DEPR_BIND
 	result_type
 	operator()(_Args&&... __args) const volatile
 	{
@@ -1119,6 +1098,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 	      _Bound_indexes());
 	}
     };
+#undef _GLIBCXX_DEPR_BIND
 
   /**
    *  @brief Class template _Bind is always a bind expression.
@@ -1222,9 +1202,7 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
     struct _Bind_helper
     : _Bind_check_arity<typename decay<_Func>::type, _BoundArgs...>
     {
-      typedef _Maybe_wrap_member_pointer<typename decay<_Func>::type>
-	__maybe_type;
-      typedef typename __maybe_type::type __func_type;
+      typedef typename decay<_Func>::type __func_type;
       typedef _Bind<__func_type(typename decay<_BoundArgs>::type...)> type;
     };
 
@@ -1245,19 +1223,15 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
     bind(_Func&& __f, _BoundArgs&&... __args)
     {
       typedef _Bind_helper<false, _Func, _BoundArgs...> __helper_type;
-      typedef typename __helper_type::__maybe_type __maybe_type;
-      typedef typename __helper_type::type __result_type;
-      return __result_type(__maybe_type::__do_wrap(std::forward<_Func>(__f)),
-			   std::forward<_BoundArgs>(__args)...);
+      return typename __helper_type::type(std::forward<_Func>(__f),
+					  std::forward<_BoundArgs>(__args)...);
     }
 
   template<typename _Result, typename _Func, typename... _BoundArgs>
     struct _Bindres_helper
     : _Bind_check_arity<typename decay<_Func>::type, _BoundArgs...>
     {
-      typedef _Maybe_wrap_member_pointer<typename decay<_Func>::type>
-	__maybe_type;
-      typedef typename __maybe_type::type __functor_type;
+      typedef typename decay<_Func>::type __functor_type;
       typedef _Bind_result<_Result,
 			   __functor_type(typename decay<_BoundArgs>::type...)>
 	type;
@@ -1273,10 +1247,8 @@  _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
     bind(_Func&& __f, _BoundArgs&&... __args)
     {
       typedef _Bindres_helper<_Result, _Func, _BoundArgs...> __helper_type;
-      typedef typename __helper_type::__maybe_type __maybe_type;
-      typedef typename __helper_type::type __result_type;
-      return __result_type(__maybe_type::__do_wrap(std::forward<_Func>(__f)),
-			   std::forward<_BoundArgs>(__args)...);
+      return typename __helper_type::type(std::forward<_Func>(__f),
+					  std::forward<_BoundArgs>(__args)...);
     }
 
   /**
diff --git a/libstdc++-v3/src/c++11/compatibility-thread-c++0x.cc b/libstdc++-v3/src/c++11/compatibility-thread-c++0x.cc
index 5ca2da8..066b08b 100644
--- a/libstdc++-v3/src/c++11/compatibility-thread-c++0x.cc
+++ b/libstdc++-v3/src/c++11/compatibility-thread-c++0x.cc
@@ -116,6 +116,19 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Replaced with inline definition in gcc-4.8.0
   __future_base::_Async_state_common::~_Async_state_common() { _M_join(); }
 
+  template<typename _Tp>
+    struct _Maybe_wrap_member_pointer;
+
+  template<typename _Tp, typename _Class>
+    struct _Maybe_wrap_member_pointer<_Tp _Class::*>
+    {
+      typedef _Mem_fn<_Tp _Class::*> type;
+
+      static constexpr type
+      __do_wrap(_Tp _Class::* __pm)
+      { return type(__pm); }
+    };
+
   template<typename _Signature>
     struct _Bind_simple;
 
diff --git a/libstdc++-v3/testsuite/20_util/bind/68912.cc b/libstdc++-v3/testsuite/20_util/bind/68912.cc
index 7d19141..a32b44b 100644
--- a/libstdc++-v3/testsuite/20_util/bind/68912.cc
+++ b/libstdc++-v3/testsuite/20_util/bind/68912.cc
@@ -45,8 +45,10 @@  void test01()
   A res = bound(1.0);
   const auto bound_c = bound;
   B res_c = bound_c(1.0);
+#if __cplusplus <= 201402L
   volatile auto bound_v = bound;
   C res_v = bound_v(1.0);
   volatile const auto bound_cv = bound;
   D res_cv = bound_cv(1.0);
+#endif
 }
diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals.cc b/libstdc++-v3/testsuite/20_util/bind/cv_quals.cc
index ebf654f..5165f87 100644
--- a/libstdc++-v3/testsuite/20_util/bind/cv_quals.cc
+++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals.cc
@@ -48,11 +48,13 @@  void test01()
   const auto b1 = std::bind(X());
   VERIFY( b1() == 1 );
 
+#if __cplusplus <= 201402L
   volatile auto b2 = std::bind(X());
   VERIFY( b2() == 2 );
 
   const volatile auto b3 = std::bind(X());
   VERIFY( b3() == 3 );
+#endif
 }
 
 void test02()
@@ -63,11 +65,13 @@  void test02()
   const auto b1 = std::bind<int>(X());
   VERIFY( b1() == 1 );
 
+#if __cplusplus <= 201402L
   volatile auto b2 = std::bind<int>(X());
   VERIFY( b2() == 2 );
 
   const volatile auto b3 = std::bind<int>(X());
   VERIFY( b3() == 3 );
+#endif
 }
 
 void test03()
@@ -78,11 +82,13 @@  void test03()
   const auto b1 = std::bind(X(), _1, 0, _2);
   VERIFY( b1(0, 0) == 1 );
 
+#if __cplusplus <= 201402L
   volatile auto b2 = std::bind(X(), _1, _2, 0);
   VERIFY( b2(0, 0) == 2 );
 
   const volatile auto b3 = std::bind(X(), _1, 0, _2);
   VERIFY( b3(0, 0) == 3 );
+#endif
 }
 
 void test04()
@@ -93,11 +99,13 @@  void test04()
   const auto b1 = std::bind<int>(X(), _1, 0, _2);
   VERIFY( b1(0, 0) == 1 );
 
+#if __cplusplus <= 201402L
   volatile auto b2 = std::bind<int>(X(), _1, _2, 0);
   VERIFY( b2(0, 0) == 2 );
 
   const volatile auto b3 = std::bind<int>(X(), _1, 0, _2);
   VERIFY( b3(0, 0) == 3 );
+#endif
 }
 
 
diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
index 9252285..cd9b204 100644
--- a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
+++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
@@ -33,11 +33,13 @@  void test01()
   const auto b0 = std::bind(X());
   VERIFY( b0() == 0 );
 
+#if __cplusplus <= 201402L
   volatile auto b1 = std::bind(X());
   VERIFY( b1() == 1 );
 
   const volatile auto b2 = std::bind(X());
   VERIFY( b2() == 2 );
+#endif
 }
 
 int main()