diff mbox

[libstdc++] Add C++17clamp

Message ID 57882528.8010501@verizon.net
State New
Headers show

Commit Message

Ed Smith-Rowland July 14, 2016, 11:50 p.m. UTC
Here is an implementation of P0025
An algorithm to "clamp" a value between a pair of boundary values.

Testing is almost finished - looks good so far.

OK if testing passes?

I didn't see a feature test in any of the SD-6 papers or P0025.
2016-07-15  Edward Smith-Rowland  <3dw4rd@verizon.net>

	Implement C++17 P0025 clamp.
	* include/bits/algorithmfwd.h: Declare clamp overloads.
	* include/bits/stl_algo.h: Implement clamp.
	* testsuite/25_algorithms/clamp/1.cc: New test.
	* testsuite/25_algorithms/clamp/2.cc: New test.
	* testsuite/25_algorithms/clamp/constexpr.cc: New test.
	* testsuite/25_algorithms/clamp/requirements/explicit_instantiation/
	1.cc: New test.
	* testsuite/25_algorithms/clamp/requirements/explicit_instantiation/
	pod.cc: New test.

Comments

Jonathan Wakely July 15, 2016, 7:34 a.m. UTC | #1
On 14/07/16 19:50 -0400, Ed Smith-Rowland wrote:
>Here is an implementation of P0025
>An algorithm to "clamp" a value between a pair of boundary values.
>
>Testing is almost finished - looks good so far.
>
>OK if testing passes?

OK for trunk, thanks.

>I didn't see a feature test in any of the SD-6 papers or P0025.

p0096r3 proposes __cpp_lib_clamp = 201603.
NightStrike July 21, 2016, 11:38 p.m. UTC | #2
On Thu, Jul 14, 2016 at 7:50 PM, Ed Smith-Rowland <3dw4rd@verizon.net> wrote:
> Here is an implementation of P0025
> An algorithm to "clamp" a value between a pair of boundary values.
>
> Testing is almost finished - looks good so far.
>
> OK if testing passes?
>
> I didn't see a feature test in any of the SD-6 papers or P0025.
>

This is not an efficient implementation.  See here:

https://gcc.gnu.org/ml/gcc-help/2014-10/msg00112.html

Which I derived from this SO answer (which is sadly not the accepted
answer at this time):

http://stackoverflow.com/a/16659263

I suggest using the very efficient method that requires a temporary.
Jonathan Wakely July 22, 2016, 7:51 a.m. UTC | #3
On 21/07/16 19:38 -0400, NightStrike wrote:
>On Thu, Jul 14, 2016 at 7:50 PM, Ed Smith-Rowland <3dw4rd@verizon.net> wrote:
>> Here is an implementation of P0025
>> An algorithm to "clamp" a value between a pair of boundary values.
>>
>> Testing is almost finished - looks good so far.
>>
>> OK if testing passes?
>>
>> I didn't see a feature test in any of the SD-6 papers or P0025.
>>
>
>This is not an efficient implementation.  See here:
>
>https://gcc.gnu.org/ml/gcc-help/2014-10/msg00112.html
>
>Which I derived from this SO answer (which is sadly not the accepted
>answer at this time):
>
>http://stackoverflow.com/a/16659263
>
>I suggest using the very efficient method that requires a temporary.

That isn't a valid implementation of std::clamp, since it performs a
copy. The template argument might not even be copyable.
Jonathan Wakely July 22, 2016, 7:55 a.m. UTC | #4
On 22/07/16 08:51 +0100, Jonathan Wakely wrote:
>On 21/07/16 19:38 -0400, NightStrike wrote:
>>On Thu, Jul 14, 2016 at 7:50 PM, Ed Smith-Rowland <3dw4rd@verizon.net> wrote:
>>>Here is an implementation of P0025
>>>An algorithm to "clamp" a value between a pair of boundary values.
>>>
>>>Testing is almost finished - looks good so far.
>>>
>>>OK if testing passes?
>>>
>>>I didn't see a feature test in any of the SD-6 papers or P0025.
>>>
>>
>>This is not an efficient implementation.  See here:
>>
>>https://gcc.gnu.org/ml/gcc-help/2014-10/msg00112.html
>>
>>Which I derived from this SO answer (which is sadly not the accepted
>>answer at this time):
>>
>>http://stackoverflow.com/a/16659263
>>
>>I suggest using the very efficient method that requires a temporary.
>
>That isn't a valid implementation of std::clamp, since it performs a
>copy. The template argument might not even be copyable.


We could possibly dispatch to such an implementation for arithmetic
types, but we wouldn't want to do it for all copyable types. There's
no way you can know whether making that local copy is expensive for an
arbitrary type, and making a copy isn't allowed anyway.
Daniel Krügler July 22, 2016, 5:38 p.m. UTC | #5
2016-07-22 9:55 GMT+02:00 Jonathan Wakely <jwakely@redhat.com>:
> On 22/07/16 08:51 +0100, Jonathan Wakely wrote:
>>
>> On 21/07/16 19:38 -0400, NightStrike wrote:
>>>
>>> On Thu, Jul 14, 2016 at 7:50 PM, Ed Smith-Rowland <3dw4rd@verizon.net>
>>> wrote:
>>>>
>>>> Here is an implementation of P0025
>>>> An algorithm to "clamp" a value between a pair of boundary values.
>>>>
>>>> Testing is almost finished - looks good so far.
>>>>
>>>> OK if testing passes?
>>>>
>>>> I didn't see a feature test in any of the SD-6 papers or P0025.
>>>>
>>>
>>> This is not an efficient implementation.  See here:
>>>
>>> https://gcc.gnu.org/ml/gcc-help/2014-10/msg00112.html
>>>
>>> Which I derived from this SO answer (which is sadly not the accepted
>>> answer at this time):
>>>
>>> http://stackoverflow.com/a/16659263
>>>
>>> I suggest using the very efficient method that requires a temporary.
>>
>>
>> That isn't a valid implementation of std::clamp, since it performs a
>> copy. The template argument might not even be copyable.
>
> We could possibly dispatch to such an implementation for arithmetic
> types, but we wouldn't want to do it for all copyable types. There's
> no way you can know whether making that local copy is expensive for an
> arbitrary type, and making a copy isn't allowed anyway.

But given that clamp is required not even to *return* copies of any of
it's arguments, it doesn't seem to be possible to do that for
arithmetic types either, unless I'm misunderstanding something very
fundamentally here. Or are you interpolating that the same performance
will result out of (ignoring constexpr) the following transformation

const float& clamp(const float& x, const float& min, const max) {
  const float& t = x < min ? min : x;
  return t > max ? max : t;
}

?
Jonathan Wakely July 22, 2016, 6:21 p.m. UTC | #6
On 22/07/16 19:38 +0200, Daniel Krügler wrote:
>2016-07-22 9:55 GMT+02:00 Jonathan Wakely <jwakely@redhat.com>:
>> On 22/07/16 08:51 +0100, Jonathan Wakely wrote:
>>>
>>> On 21/07/16 19:38 -0400, NightStrike wrote:
>>>>
>>>> On Thu, Jul 14, 2016 at 7:50 PM, Ed Smith-Rowland <3dw4rd@verizon.net>
>>>> wrote:
>>>>>
>>>>> Here is an implementation of P0025
>>>>> An algorithm to "clamp" a value between a pair of boundary values.
>>>>>
>>>>> Testing is almost finished - looks good so far.
>>>>>
>>>>> OK if testing passes?
>>>>>
>>>>> I didn't see a feature test in any of the SD-6 papers or P0025.
>>>>>
>>>>
>>>> This is not an efficient implementation.  See here:
>>>>
>>>> https://gcc.gnu.org/ml/gcc-help/2014-10/msg00112.html
>>>>
>>>> Which I derived from this SO answer (which is sadly not the accepted
>>>> answer at this time):
>>>>
>>>> http://stackoverflow.com/a/16659263
>>>>
>>>> I suggest using the very efficient method that requires a temporary.
>>>
>>>
>>> That isn't a valid implementation of std::clamp, since it performs a
>>> copy. The template argument might not even be copyable.
>>
>> We could possibly dispatch to such an implementation for arithmetic
>> types, but we wouldn't want to do it for all copyable types. There's
>> no way you can know whether making that local copy is expensive for an
>> arbitrary type, and making a copy isn't allowed anyway.
>
>But given that clamp is required not even to *return* copies of any of
>it's arguments, it doesn't seem to be possible to do that for
>arithmetic types either, unless I'm misunderstanding something very
>fundamentally here. Or are you interpolating that the same performance
>will result out of (ignoring constexpr) the following transformation
>
>const float& clamp(const float& x, const float& min, const max) {
>  const float& t = x < min ? min : x;
>  return t > max ? max : t;
>}
>
>?

No, I was just overlooking the fact that of course we can't make a
copy if we have to return a reference to one of the arguments.
NightStrike July 22, 2016, 9:12 p.m. UTC | #7
Would it be possible to fix the missed optimization to give the better
asm without having to do -ffast-math as mentioned here:

https://gcc.gnu.org/ml/gcc-help/2014-11/msg00034.html

If so, then the proposed implementation would be optimized for the simple case.


On Fri, Jul 22, 2016 at 3:55 AM, Jonathan Wakely <jwakely@redhat.com> wrote:
> On 22/07/16 08:51 +0100, Jonathan Wakely wrote:
>>
>> On 21/07/16 19:38 -0400, NightStrike wrote:
>>>
>>> On Thu, Jul 14, 2016 at 7:50 PM, Ed Smith-Rowland <3dw4rd@verizon.net>
>>> wrote:
>>>>
>>>> Here is an implementation of P0025
>>>> An algorithm to "clamp" a value between a pair of boundary values.
>>>>
>>>> Testing is almost finished - looks good so far.
>>>>
>>>> OK if testing passes?
>>>>
>>>> I didn't see a feature test in any of the SD-6 papers or P0025.
>>>>
>>>
>>> This is not an efficient implementation.  See here:
>>>
>>> https://gcc.gnu.org/ml/gcc-help/2014-10/msg00112.html
>>>
>>> Which I derived from this SO answer (which is sadly not the accepted
>>> answer at this time):
>>>
>>> http://stackoverflow.com/a/16659263
>>>
>>> I suggest using the very efficient method that requires a temporary.
>>
>>
>> That isn't a valid implementation of std::clamp, since it performs a
>> copy. The template argument might not even be copyable.
>
>
>
> We could possibly dispatch to such an implementation for arithmetic
> types, but we wouldn't want to do it for all copyable types. There's
> no way you can know whether making that local copy is expensive for an
> arbitrary type, and making a copy isn't allowed anyway.
>
Jonathan Wakely July 22, 2016, 11:59 p.m. UTC | #8
On 22/07/16 17:12 -0400, NightStrike wrote:
>Would it be possible to fix the missed optimization to give the better
>asm without having to do -ffast-math as mentioned here:
>
>https://gcc.gnu.org/ml/gcc-help/2014-11/msg00034.html
>
>If so, then the proposed implementation would be optimized for the simple case.

If you want a missed optimization fixed then you should report it to
bugzilla, not on these lists.
diff mbox

Patch

Index: include/bits/algorithmfwd.h
===================================================================
--- include/bits/algorithmfwd.h	(revision 238302)
+++ include/bits/algorithmfwd.h	(working copy)
@@ -48,6 +48,7 @@ 
     all_of (C++0x)
     any_of (C++0x)
     binary_search
+    clamp (C++17)
     copy
     copy_backward
     copy_if (C++0x)
@@ -208,6 +209,18 @@ 
     bool 
     binary_search(_FIter, _FIter, const _Tp&, _Compare);
 
+#if __cplusplus > 201402L
+  template<typename _Tp>
+    _GLIBCXX14_CONSTEXPR
+    const _Tp&
+    clamp(const _Tp&, const _Tp&, const _Tp&);
+
+  template<typename _Tp, typename _Compare>
+    _GLIBCXX14_CONSTEXPR
+    const _Tp&
+    clamp(const _Tp&, const _Tp&, const _Tp&, _Compare);
+#endif
+
   template<typename _IIter, typename _OIter>
     _OIter 
     copy(_IIter, _IIter, _OIter);
Index: include/bits/stl_algo.h
===================================================================
--- include/bits/stl_algo.h	(revision 238302)
+++ include/bits/stl_algo.h	(working copy)
@@ -3698,8 +3698,44 @@ 
       return std::__is_permutation(__first1, __last1, __first2, __last2,
 				   __gnu_cxx::__ops::__iter_comp_iter(__pred));
     }
-#endif
 
+#if __cplusplus > 201402L
+  /**
+   *  @brief  Returns the value clamped between lo and hi.
+   *  @ingroup sorting_algorithms
+   *  @param  __val  A value of arbitrary type.
+   *  @param  __lo   A lower limit of arbitrary type.
+   *  @param  __hi   An upper limit of arbitrary type.
+   *  @return max(__val, __lo) if __val < __hi or min(__val, __hi) otherwise.
+   */
+  template<typename _Tp>
+    constexpr const _Tp&
+    clamp(const _Tp& __val, const _Tp& __lo, const _Tp& __hi)
+    {
+      __glibcxx_assert(!(__hi < __lo));
+      return (__val < __lo) ? __lo : (__hi < __val) ? __hi : __val;
+    }
+
+  /**
+   *  @brief  Returns the value clamped between lo and hi.
+   *  @ingroup sorting_algorithms
+   *  @param  __val   A value of arbitrary type.
+   *  @param  __lo    A lower limit of arbitrary type.
+   *  @param  __hi    An upper limit of arbitrary type.
+   *  @param  __comp  A comparison functor.
+   *  @return max(__val, __lo, __comp) if __comp(__val, __hi)
+   *	      or min(__val, __hi, __comp) otherwise.
+   */
+  template<typename _Tp, typename _Compare>
+    constexpr const _Tp&
+    clamp(const _Tp& __val, const _Tp& __lo, const _Tp& __hi, _Compare __comp)
+    {
+      __glibcxx_assert(!__comp(__hi, __lo));
+      return __comp(__val, __lo) ? __lo : __comp(__hi, __val) ? __hi : __val;
+    }
+#endif // C++17
+#endif // C++14
+
 #ifdef _GLIBCXX_USE_C99_STDINT_TR1
   /**
    *  @brief Shuffle the elements of a sequence using a uniform random
Index: testsuite/25_algorithms/clamp/1.cc
===================================================================
--- testsuite/25_algorithms/clamp/1.cc	(nonexistent)
+++ testsuite/25_algorithms/clamp/1.cc	(working copy)
@@ -0,0 +1,48 @@ 
+// { dg-options "-std=gnu++17" }
+
+// 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 <algorithm>
+#include <functional>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  const int x = std::clamp(1, 2, 4);
+  const int y = std::clamp(3, 2, 4);
+  const int z = std::clamp(5, 2, 4);
+  VERIFY( x == 2 );
+  VERIFY( y == 3 );
+  VERIFY( z == 4 );
+
+  const int xc = std::clamp(1, 2, 4, std::greater<int>());
+  const int yc = std::clamp(3, 2, 4, std::greater<int>());
+  const int zc = std::clamp(5, 2, 4, std::greater<int>());
+  VERIFY( xc == 4 );
+  VERIFY( yc == 2 );
+  VERIFY( zc == 2 );
+}
+
+int
+main()
+{
+  test01();
+  return 0;
+}
Index: testsuite/25_algorithms/clamp/2.cc
===================================================================
--- testsuite/25_algorithms/clamp/2.cc	(nonexistent)
+++ testsuite/25_algorithms/clamp/2.cc	(working copy)
@@ -0,0 +1,102 @@ 
+// { dg-options "-std=gnu++17" }
+
+// Copyright (C) 2000-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 <algorithm>
+#include <functional>
+#include <testsuite_hooks.h>
+
+template<typename T>
+  struct A { static const T a; };
+
+template<typename T>
+const T A<T>::a = T(3);
+
+void test02()
+{
+  bool test __attribute__((unused)) = true;
+
+  VERIFY( 3 == std::clamp(A<int>::a, 2, 4) );
+  VERIFY( 2 == std::clamp(A<int>::a, 1, 2) );
+  VERIFY( 4 == std::clamp(A<int>::a, 4, 6) );
+
+  VERIFY( 3u == std::clamp(A<unsigned int>::a, 2u, 4u) );
+  VERIFY( 2u == std::clamp(A<unsigned int>::a, 1u, 2u) );
+  VERIFY( 4u == std::clamp(A<unsigned int>::a, 4u, 6u) );
+
+  VERIFY( 3l == std::clamp(A<long>::a, 2l, 4l) );
+  VERIFY( 2l == std::clamp(A<long>::a, 1l, 2l) );
+  VERIFY( 4l == std::clamp(A<long>::a, 4l, 6l) );
+
+  VERIFY( 3ul == std::clamp(A<unsigned long>::a, 2ul, 4ul) );
+  VERIFY( 2ul == std::clamp(A<unsigned long>::a, 1ul, 2ul) );
+  VERIFY( 4ul == std::clamp(A<unsigned long>::a, 4ul, 6ul) );
+
+#ifdef _GLIBCXX_USE_LONG_LONG
+  VERIFY( 3ll == std::clamp(A<long long>::a, 2ll, 4ll) );
+  VERIFY( 2ll == std::clamp(A<long long>::a, 1ll, 2ll) );
+  VERIFY( 4ll == std::clamp(A<long long>::a, 4ll, 6ll) );
+
+  VERIFY( 3ull == std::clamp(A<unsigned long long>::a, 2ull, 4ull) );
+  VERIFY( 2ull == std::clamp(A<unsigned long long>::a, 1ull, 2ull) );
+  VERIFY( 4ull == std::clamp(A<unsigned long long>::a, 4ull, 6ull) );
+#endif
+
+  VERIFY( (short)3 == std::clamp(A<short>::a, (short)2, (short)4) );
+  VERIFY( (short)2 == std::clamp(A<short>::a, (short)1, (short)2) );
+  VERIFY( (short)4 == std::clamp(A<short>::a, (short)4, (short)6) );
+
+  VERIFY( (unsigned short)3 == std::clamp(A<unsigned short>::a, (unsigned short)2, (unsigned short)4) );
+  VERIFY( (unsigned short)2 == std::clamp(A<unsigned short>::a, (unsigned short)1, (unsigned short)2) );
+  VERIFY( (unsigned short)4 == std::clamp(A<unsigned short>::a, (unsigned short)4, (unsigned short)6) );
+
+  VERIFY( (char)3 == std::clamp(A<char>::a, (char)2, (char)4) );
+  VERIFY( (char)2 == std::clamp(A<char>::a, (char)1, (char)2) );
+  VERIFY( (char)4 == std::clamp(A<char>::a, (char)4, (char)6) );
+
+  VERIFY( (signed char)3 == std::clamp(A<signed char>::a, (signed char)2, (signed char)4) );
+  VERIFY( (signed char)2 == std::clamp(A<signed char>::a, (signed char)1, (signed char)2) );
+  VERIFY( (signed char)4 == std::clamp(A<signed char>::a, (signed char)4, (signed char)6) );
+
+  VERIFY( (unsigned char)3 == std::clamp(A<unsigned char>::a, (unsigned char)2, (unsigned char)4) );
+  VERIFY( (unsigned char)2 == std::clamp(A<unsigned char>::a, (unsigned char)1, (unsigned char)2) );
+  VERIFY( (unsigned char)4 == std::clamp(A<unsigned char>::a, (unsigned char)4, (unsigned char)6) );
+
+  VERIFY( (wchar_t)3 == std::clamp(A<wchar_t>::a, (wchar_t)2, (wchar_t)4) );
+  VERIFY( (wchar_t)2 == std::clamp(A<wchar_t>::a, (wchar_t)1, (wchar_t)2) );
+  VERIFY( (wchar_t)4 == std::clamp(A<wchar_t>::a, (wchar_t)4, (wchar_t)6) );
+
+  VERIFY( 3.0 == std::clamp(A<double>::a, 2.0, 4.0) );
+  VERIFY( 2.0 == std::clamp(A<double>::a, 1.0, 2.0) );
+  VERIFY( 4.0 == std::clamp(A<double>::a, 4.0, 6.0) );
+
+  VERIFY( 3.0f == std::clamp(A<float>::a, 2.0f, 4.0f) );
+  VERIFY( 2.0f == std::clamp(A<float>::a, 1.0f, 2.0f) );
+  VERIFY( 4.0f == std::clamp(A<float>::a, 4.0f, 6.0f) );
+
+  VERIFY( 3.0l == std::clamp(A<long double>::a, 2.0l, 4.0l) );
+  VERIFY( 2.0l == std::clamp(A<long double>::a, 1.0l, 2.0l) );
+  VERIFY( 4.0l == std::clamp(A<long double>::a, 4.0l, 6.0l) );
+}
+
+int
+main()
+{
+  test02();
+  return 0;
+}
Index: testsuite/25_algorithms/clamp/constexpr.cc
===================================================================
--- testsuite/25_algorithms/clamp/constexpr.cc	(nonexistent)
+++ testsuite/25_algorithms/clamp/constexpr.cc	(working copy)
@@ -0,0 +1,25 @@ 
+// { dg-options "-std=gnu++17" }
+// { dg-do compile }
+
+// 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 <algorithm>
+#include <functional>
+
+static_assert(std::clamp(2, 0, 1) == 1, "");
+static_assert(std::clamp(2, 0, 1, std::greater<int>()) == 0, "");
Index: testsuite/25_algorithms/clamp/requirements/explicit_instantiation/1.cc
===================================================================
--- testsuite/25_algorithms/clamp/requirements/explicit_instantiation/1.cc	(nonexistent)
+++ testsuite/25_algorithms/clamp/requirements/explicit_instantiation/1.cc	(working copy)
@@ -0,0 +1,38 @@ 
+// { dg-options "-std=gnu++17" }
+// { dg-do compile }
+
+// 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 <algorithm>
+#include <functional>
+#include <testsuite_api.h>
+
+namespace std
+{
+  using __gnu_test::NonDefaultConstructible;
+
+  typedef NonDefaultConstructible	value_type;
+  typedef value_type*			iterator_type;
+  typedef std::less<value_type>		compare_type;
+
+  template const value_type& clamp(const value_type&,
+				   const value_type&, const value_type&);
+  template const value_type& clamp(const value_type&,
+				   const value_type&, const value_type&,
+				   compare_type);
+}
Index: testsuite/25_algorithms/clamp/requirements/explicit_instantiation/pod.cc
===================================================================
--- testsuite/25_algorithms/clamp/requirements/explicit_instantiation/pod.cc	(nonexistent)
+++ testsuite/25_algorithms/clamp/requirements/explicit_instantiation/pod.cc	(working copy)
@@ -0,0 +1,38 @@ 
+// { dg-options "-std=gnu++17" }
+// { dg-do compile }
+
+// 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 <algorithm>
+#include <testsuite_character.h>
+
+namespace std
+{
+  using __gnu_test::pod_int;
+
+  typedef pod_int 		value_type;
+  typedef value_type* 		iterator_type;
+  typedef std::less<value_type> compare_type;
+
+  template const value_type& clamp(const value_type&,
+				   const value_type&, const value_type&);
+  template const value_type& clamp(const value_type&,
+				   const value_type&, const value_type&,
+				   compare_type);
+}