What should we do about Boost-style "concept checks"?

Jonathan Wakely jwakely@redhat.com
Wed May 21 11:59:25 GMT 2025


On Wed, 21 May 2025 at 10:04, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> We have this extension:
> https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_compile_checks.html
> The macros like __glibcxx_function_requires are defined in
> include/bits/concept_check.h and the code for the actual "concepts" is
> in include/bits/boost_concept_check.h
> The __glibcxx_function_requires macro is used widely in <algorithm>
> and <random>, and __glibcxx_class_requires[1234] macros are used in
> containers.
>
> There is also the --enable-concept-checks configure option, which
> surprisingly doesn't cause bootstrap to fail, but is probably a very
> bad idea to enable by default.
>
> As noted at the link above, the concept checks only verify the C++03
> requirements, and are often unusable with C++11 and newer. They don't
> know about move semantics, so the checks incorrectly require copyable
> types in places where the library only really requires movable. They
> don't know about C++20 iterators, so algorithms that actually work
> fine with C++20 iterators fail their concept checks.
>
> We've had 15 years to update the checks to work for C++11 and nobody
> bothered. I doubt anybody is using this extension now.
>
> But they do sometimes help find bugs, and we *could* probably update
> them to match modern semantics (e.g. replace SGIAssignable with
> Cpp17MoveAssignable things like that). And we could change
> __glibcxx_function_requires to be a static_assert for C++11 and later.
> And we could rewrite e.g. _SGIAssignable::__constraints() to use a
> requires-clause instead of checking the constraints in the function
> body.
>
> Or we could deprecate and remove the checks, but I'd like to see what
> people think about modernizing them instead of removing them.

For example, here's a proof of concept that rewrites the concept
checks in terms of static assertions when compiled as C++11 or later.

I haven't updated the various iterator and sequence container
concepts, as that would take more time than I am willing to spend on
this for now. But I think this is an interesting direction. Doing it
this way expands the macros like __glibcxx_function_requires into a
static_assert, rather than using the fairly complex C++98-compatible
approach of calling a function which takes the address of a function
template which instantiates the body of that function template, which
checks the required expressions are valid.

With static_assert and <type_traits> many of the checks can be
expressed more succinctly, rather than actually writing out all those
expressions as real code. For C++20 we could also consider placing the
requirements directly on the _FooConcept classes as a requires-clause
(although IMHO the result is less user-friendly than a clear
static_assert).

However, even if we decide to go in this direction, there would be a
lot of work to audit all the existing uses of
__glibcxx_function_requires and __glibcxx_class_requires[1234] to
check if the requirements need to be updated for C++11 and later. At
the very least, most uses of _CopyConstructible and _SGIAssignable
need to be checked to see if they should be relaxed to support
move-only types.
-------------- next part --------------
diff --git a/libstdc++-v3/include/bits/boost_concept_check.h b/libstdc++-v3/include/bits/boost_concept_check.h
index a1f488dd8af1..774c1554413a 100644
--- a/libstdc++-v3/include/bits/boost_concept_check.h
+++ b/libstdc++-v3/include/bits/boost_concept_check.h
@@ -72,11 +72,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #define _IsUnused __attribute__ ((__unused__))
 
+#if __cplusplus < 201103L
 // When the C-C code is in use, we would like this function to do as little
 // as possible at runtime, use as few resources as possible, and hopefully
 // be elided out of existence... hmmm.
 template <class _Concept>
-_GLIBCXX14_CONSTEXPR inline void __function_requires()
+inline void __function_requires()
 {
   void (_Concept::*__x)() _IsUnused = &_Concept::__constraints;
 }
@@ -121,21 +122,42 @@ void __error_type_must_be_a_signed_integer_type();
   &_ns::_concept <_type_var1,_type_var2,_type_var3,_type_var4>::__constraints> \
     _concept_checking_typedef##_type_var1##_type_var2##_type_var3##_type_var4##_concept
 
-
 template <class _Tp1, class _Tp2>
 struct _Aux_require_same { };
 
 template <class _Tp>
 struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
 
+#endif // C++98
+
   template <class _Tp1, class _Tp2>
   struct _SameTypeConcept
   {
+#if __cplusplus >= 201103L
+    static_assert(__is_same(_Tp1, _Tp2), "types must be the same");
+#else
     void __constraints() {
       typedef typename _Aux_require_same<_Tp1, _Tp2>::_Type _Required;
     }
+#endif
   };
 
+#if __cplusplus >= 201103L
+  template <class _Tp>
+    struct _IntegerConcept {
+      static_assert(std::is_integral<_Tp>::value, "must be an integer type");
+    };
+  template <class _Tp>
+    struct _SignedIntegerConcept : _IntegerConcept<_Tp> {
+      static_assert(std::is_signed<_Tp>::value,
+		    "must be an unsigned integer type");
+    };
+  template <class _Tp>
+    struct _UnsignedIntegerConcept : _IntegerConcept<_Tp> {
+      static_assert(std::is_unsigned<_Tp>::value,
+		    "must be an unsigned integer type");
+    };
+#else
   template <class _Tp>
   struct _IntegerConcept {
     void __constraints() {
@@ -177,6 +199,7 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     { void __constraints() {} };
   template <> struct _UnsignedIntegerConcept<unsigned long long>
     { void __constraints() {} };
+#endif
 
   //===========================================================================
   // Basic Concepts
@@ -184,14 +207,25 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
   template <class _Tp>
   struct _DefaultConstructibleConcept
   {
+#if __cplusplus >= 201103L
+    static_assert(std::is_constructible<_Tp>::value,
+		  "must be default constructible");
+#else
     void __constraints() {
       _Tp __a _IsUnused;                // require default constructor
     }
+#endif
   };
 
   template <class _Tp>
   struct _AssignableConcept
   {
+#if __cplusplus >= 201103L
+    static_assert(std::is_assignable<_Tp&, _Tp&>::value,
+		  "must be assignable");
+    static_assert(std::is_assignable<_Tp&, const _Tp&>::value,
+		  "must be assignable");
+#else
     void __constraints() {
       __a = __a;                        // require assignment operator
       __const_constraints(__a);
@@ -202,26 +236,40 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     _Tp __a;
     // possibly should be "Tp* a;" and then dereference "a" in constraint
     // functions?  present way would require a default ctor, i think...
+#endif
   };
 
   template <class _Tp>
   struct _CopyConstructibleConcept
   {
+#if __cplusplus >= 201103L
+    static_assert(std::is_constructible<_Tp, _Tp&>::value,
+		  "must be copy constructible");
+    static_assert(std::is_constructible<_Tp, const _Tp>::value,
+		  "must be copy constructible");
+    static_assert(std::is_constructible<_Tp, const _Tp&>::value,
+		  "must be copy constructible");
+#else
     void __constraints() {
       _Tp __a(__b);                     // require copy constructor
-      _Tp* __ptr _IsUnused = &__a;      // require address of operator
+      // _Tp* __ptr _IsUnused = &__a;      // require address of operator
       __const_constraints(__a);
     }
     void __const_constraints(const _Tp& __a) {
       _Tp __c _IsUnused(__a);           // require const copy constructor
-      const _Tp* __ptr _IsUnused = &__a; // require const address of operator
+      // const _Tp* __ptr _IsUnused = &__a; // require const address of operator
     }
     _Tp __b;
+#endif
   };
 
   // The SGI STL version of Assignable requires copy constructor and operator=
   template <class _Tp>
   struct _SGIAssignableConcept
+#if __cplusplus >= 201103L
+  : _AssignableConcept<_Tp>, _CopyConstructibleConcept<_Tp>
+  { };
+#else
   {
     void __constraints() {
       _Tp __b _IsUnused(__a);
@@ -234,14 +282,20 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     }
     _Tp __a;
   };
+#endif
 
   template <class _From, class _To>
   struct _ConvertibleConcept
   {
+#if __cplusplus >= 201103L
+    static_assert(std::is_convertible<_From, _To>::value,
+		  "must be implicitly convertible to other type");
+#else
     void __constraints() {
       _To __y _IsUnused = __x;
     }
     _From __x;
+#endif
   };
 
   // The C++ standard requirements for many concepts talk about return
@@ -331,6 +385,48 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
   //===========================================================================
   // Function Object Concepts
 
+#if __cplusplus >= 201103L
+#if 0 // __cpp_concepts
+  // This might be useful for the more complex checks for iterator types below,
+  // but for this simple case using static_assert gives nicer diagnostics.
+  template <class _Func, class _Return, class... _Args>
+    requires std::is_invocable_r_v<_Return, _Func&, _Args...>
+  struct _CallableConcept
+  { };
+#else
+  template <class _Func, class _Return, class... _Args>
+  struct _CallableConcept
+  {
+    static_assert(std::__is_invocable<_Func&, _Args...>::value,
+		  "must be invocable");
+    static_assert(std::is_convertible<std::__invoke_result_t<_Func&, _Args...>,
+				      _Return>::value,
+		  "invocation result must be convertible to result type");
+  };
+
+  template <class _Func, class... _Args>
+  struct _CallableConcept<_Func, void, _Args...>
+  {
+    static_assert(std::__is_invocable<_Func&, _Args...>::value,
+		  "must be invocable");
+  };
+#endif
+
+  template <class _Func, class _Return>
+  using _GeneratorConcept = _CallableConcept<_Func, _Return>;
+  template <class _Func, class _Return, class _Arg>
+  using _UnaryFunctionConcept = _CallableConcept<_Func, _Return, _Arg>;
+  template <class _Func, class _Return, class _Arg1, class _Arg2>
+  using _BinaryFunctionConcept = _CallableConcept<_Func, _Return, _Arg1, _Arg2>;
+  template <class _Func, class _Arg>
+  using _UnaryPredicateConcept = _CallableConcept<_Func, bool, _Arg>;
+  template <class _Func, class _Arg1, class _Arg2>
+  using _BinaryPredicateConcept = _CallableConcept<_Func, bool, _Arg1, _Arg2>;
+  template <class _Func, class _Arg1, class _Arg2>
+  using _Const_BinaryPredicateConcept
+      = _BinaryPredicateConcept<const _Func, _Arg1, _Arg2>;
+#else
+
   template <class _Func, class _Return>
   struct _GeneratorConcept
   {
@@ -429,6 +525,7 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     _First __a;
     _Second __b;
   };
+#endif
 
   //===========================================================================
   // Iterator Concepts
@@ -436,6 +533,7 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
   template <class _Tp>
   struct _TrivialIteratorConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
 //    __function_requires< _DefaultConstructibleConcept<_Tp> >();
       __function_requires< _AssignableConcept<_Tp> >();
@@ -444,21 +542,25 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
       (void)*__i;                       // require dereference operator
     }
     _Tp __i;
+#endif
   };
 
   template <class _Tp>
   struct _Mutable_TrivialIteratorConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _TrivialIteratorConcept<_Tp> >();
       *__i = *__j;                      // require dereference and assignment
     }
     _Tp __i, __j;
+#endif
   };
 
   template <class _Tp>
   struct _InputIteratorConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _TrivialIteratorConcept<_Tp> >();
       // require iterator_traits typedef's
@@ -474,11 +576,13 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
       __i++;                            // require postincrement operator
     }
     _Tp __i;
+#endif
   };
 
   template <class _Tp, class _ValueT>
   struct _OutputIteratorConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _AssignableConcept<_Tp> >();
       ++__i;                            // require preincrement operator
@@ -489,6 +593,7 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     // Use a function pointer here so no definition of the function needed.
     // Just need something that returns a _ValueT (which might be a reference).
     _ValueT (*__val)();
+#endif
   };
 
   template<typename _Tp>
@@ -519,6 +624,7 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
   template <class _Tp, bool = _Is_vector_bool_iterator<_Tp>::__value>
   struct _ForwardIteratorReferenceConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
 #if __cplusplus >= 201103L
       typedef typename std::iterator_traits<_Tp>::reference _Ref;
@@ -526,30 +632,37 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
 	  "reference type of a forward iterator must be a real reference");
 #endif
     }
+#endif
   };
 
   template <class _Tp, bool = _Is_vector_bool_iterator<_Tp>::__value>
   struct _Mutable_ForwardIteratorReferenceConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       typedef typename std::iterator_traits<_Tp>::reference _Ref;
       typedef typename std::iterator_traits<_Tp>::value_type _Val;
       __function_requires< _SameTypeConcept<_Ref, _Val&> >();
     }
+#endif
   };
 
   // vector<bool> iterators are not real forward iterators, but we ignore that.
   template <class _Tp>
   struct _ForwardIteratorReferenceConcept<_Tp, true>
   {
+#if __cplusplus < 201103L
     void __constraints() { }
+#endif
   };
 
   // vector<bool> iterators are not real forward iterators, but we ignore that.
   template <class _Tp>
   struct _Mutable_ForwardIteratorReferenceConcept<_Tp, true>
   {
+#if __cplusplus < 201103L
     void __constraints() { }
+#endif
   };
 
 #pragma GCC diagnostic push
@@ -558,6 +671,7 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
   template <class _Tp>
   struct _ForwardIteratorConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _InputIteratorConcept<_Tp> >();
       __function_requires< _DefaultConstructibleConcept<_Tp> >();
@@ -572,11 +686,13 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
       _Ref __r2 = *__i++;
     }
     _Tp __i;
+#endif
   };
 
   template <class _Tp>
   struct _Mutable_ForwardIteratorConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _ForwardIteratorConcept<_Tp> >();
       typedef typename std::iterator_traits<_Tp>::reference _Ref;
@@ -584,11 +700,13 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
       __function_requires< _Mutable_ForwardIteratorReferenceConcept<_Tp> >();
     }
     _Tp __i;
+#endif
   };
 
   template <class _Tp>
   struct _BidirectionalIteratorConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _ForwardIteratorConcept<_Tp> >();
       __function_requires< _ConvertibleConcept<
@@ -600,22 +718,26 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
       _Ref __r = *__j--;
     }
     _Tp __i;
+#endif
   };
 
   template <class _Tp>
   struct _Mutable_BidirectionalIteratorConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _BidirectionalIteratorConcept<_Tp> >();
       __function_requires< _Mutable_ForwardIteratorConcept<_Tp> >();
     }
     _Tp __i;
+#endif
   };
 
 
   template <class _Tp>
   struct _RandomAccessIteratorConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _BidirectionalIteratorConcept<_Tp> >();
       __function_requires< _ComparableConcept<_Tp> >();
@@ -635,17 +757,20 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     _Tp __a, __b;
     _Tp __i, __j;
     typename std::iterator_traits<_Tp>::difference_type __n;
+#endif
   };
 
   template <class _Tp>
   struct _Mutable_RandomAccessIteratorConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _RandomAccessIteratorConcept<_Tp> >();
       __function_requires< _Mutable_BidirectionalIteratorConcept<_Tp> >();
     }
     _Tp __i;
     typename std::iterator_traits<_Tp>::difference_type __n;
+#endif
   };
 
 #pragma GCC diagnostic pop
@@ -656,6 +781,7 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
   template <class _Container>
   struct _ContainerConcept
   {
+#if __cplusplus < 201103L
     typedef typename _Container::value_type _Value_type;
     typedef typename _Container::difference_type _Difference_type;
     typedef typename _Container::size_type _Size_type;
@@ -676,11 +802,13 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     bool __b;
     _Const_iterator __i;
     _Size_type __n;
+#endif
   };
 
   template <class _Container>
   struct _Mutable_ContainerConcept
   {
+#if __cplusplus < 201103L
     typedef typename _Container::value_type _Value_type;
     typedef typename _Container::reference _Reference;
     typedef typename _Container::iterator _Iterator;
@@ -697,32 +825,38 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     }
     _Iterator __i;
     _Container __c, __c2;
+#endif
   };
 
   template <class _ForwardContainer>
   struct _ForwardContainerConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _ContainerConcept<_ForwardContainer> >();
       typedef typename _ForwardContainer::const_iterator _Const_iterator;
       __function_requires< _ForwardIteratorConcept<_Const_iterator> >();
     }
+#endif
   };
 
   template <class _ForwardContainer>
   struct _Mutable_ForwardContainerConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _ForwardContainerConcept<_ForwardContainer> >();
       __function_requires< _Mutable_ContainerConcept<_ForwardContainer> >();
       typedef typename _ForwardContainer::iterator _Iterator;
       __function_requires< _Mutable_ForwardIteratorConcept<_Iterator> >();
     }
+#endif
   };
 
   template <class _ReversibleContainer>
   struct _ReversibleContainerConcept
   {
+#if __cplusplus < 201103L
     typedef typename _ReversibleContainer::const_iterator _Const_iterator;
     typedef typename _ReversibleContainer::const_reverse_iterator
       _Const_reverse_iterator;
@@ -737,11 +871,13 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
       _Const_reverse_iterator __i = __c.rbegin();
       __i = __c.rend();
     }
+#endif
   };
 
   template <class _ReversibleContainer>
   struct _Mutable_ReversibleContainerConcept
   {
+#if __cplusplus < 201103L
     typedef typename _ReversibleContainer::iterator _Iterator;
     typedef typename _ReversibleContainer::reverse_iterator _Reverse_iterator;
 
@@ -757,11 +893,13 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
       __i = __c.rend();
     }
     _ReversibleContainer __c;
+#endif
   };
 
   template <class _RandomAccessContainer>
   struct _RandomAccessContainerConcept
   {
+#if __cplusplus < 201103L
     typedef typename _RandomAccessContainer::size_type _Size_type;
     typedef typename _RandomAccessContainer::const_reference _Const_reference;
     typedef typename _RandomAccessContainer::const_iterator _Const_iterator;
@@ -779,11 +917,13 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
       _Const_reference __r _IsUnused = __c[__n];
     }
     _Size_type __n;
+#endif
   };
 
   template <class _RandomAccessContainer>
   struct _Mutable_RandomAccessContainerConcept
   {
+#if __cplusplus < 201103L
     typedef typename _RandomAccessContainer::size_type _Size_type;
     typedef typename _RandomAccessContainer::reference _Reference;
     typedef typename _RandomAccessContainer::iterator _Iterator;
@@ -802,12 +942,14 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     }
     _Size_type __i;
     _RandomAccessContainer __c;
+#endif
   };
 
   // A Sequence is inherently mutable
   template <class _Sequence>
   struct _SequenceConcept
   {
+#if __cplusplus < 201103L
     typedef typename _Sequence::reference _Reference;
     typedef typename _Sequence::const_reference _Const_reference;
 
@@ -840,11 +982,13 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     typename _Sequence::size_type __n;
     typename _Sequence::value_type *__first, *__last;
     typename _Sequence::iterator __p, __q;
+#endif
   };
 
   template <class _FrontInsertionSequence>
   struct _FrontInsertionSequenceConcept
   {
+#if __cplusplus < 201103L
     void __constraints() {
       __function_requires< _SequenceConcept<_FrontInsertionSequence> >();
 
@@ -853,11 +997,13 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     }
     _FrontInsertionSequence __c;
     typename _FrontInsertionSequence::value_type __t;
+#endif
   };
 
   template <class _BackInsertionSequence>
   struct _BackInsertionSequenceConcept
   {
+#if __cplusplus < 201103L
     typedef typename _BackInsertionSequence::reference _Reference;
     typedef typename _BackInsertionSequence::const_reference _Const_reference;
 
@@ -873,6 +1019,7 @@ struct _Aux_require_same<_Tp,_Tp> { typedef _Tp _Type; };
     };
     _BackInsertionSequence __c;
     typename _BackInsertionSequence::value_type __t;
+#endif
   };
 
 _GLIBCXX_END_NAMESPACE_VERSION
@@ -882,5 +1029,3 @@ _GLIBCXX_END_NAMESPACE_VERSION
 #undef _IsUnused
 
 #endif // _GLIBCXX_BOOST_CONCEPT_CHECK
-
-
diff --git a/libstdc++-v3/include/bits/concept_check.h b/libstdc++-v3/include/bits/concept_check.h
index 3e6806785d97..9512c57c428e 100644
--- a/libstdc++-v3/include/bits/concept_check.h
+++ b/libstdc++-v3/include/bits/concept_check.h
@@ -70,7 +70,23 @@
 // parenthesis to hide the commas, because "debug::(Temp<Foo,Bar>)" isn't
 // a valid instantiation pattern.  Thus, we steal a feature from C99.
 
-#define __glibcxx_function_requires(...)                                 \
+#if __cplusplus >= 201103L
+
+# define __glibcxx_function_requires(...)                                 \
+  static_assert(sizeof(__gnu_cxx::__VA_ARGS__) != 0, #__VA_ARGS__);
+#define __glibcxx_class_requires_impl(_C, ...)                            \
+  static_assert(sizeof(__gnu_cxx::_C<__VA_ARGS__>) != 0, #_C "<" #__VA_ARGS__ ">");
+#define __glibcxx_class_requires(_a,_C)                                   \
+  __glibcxx_class_requires_impl(_C,_a)
+#define __glibcxx_class_requires2(_a,_b,_C)                              \
+  __glibcxx_class_requires_impl(_C, _a, _b)
+#define __glibcxx_class_requires3(_a,_b,_c,_C)                           \
+  __glibcxx_class_requires_impl(_C, _a, _b, _c)
+#define __glibcxx_class_requires4(_a,_b,_c,_d,_C)                        \
+  __glibcxx_class_requires_impl(_C, _a, _b, _c, _d)
+
+#else
+# define __glibcxx_function_requires(...)                                 \
             __gnu_cxx::__function_requires< __gnu_cxx::__VA_ARGS__ >();
 #define __glibcxx_class_requires(_a,_C)                                  \
             _GLIBCXX_CLASS_REQUIRES(_a, __gnu_cxx, _C);
@@ -80,6 +96,7 @@
             _GLIBCXX_CLASS_REQUIRES3(_a, _b, _c, __gnu_cxx, _C);
 #define __glibcxx_class_requires4(_a,_b,_c,_d,_C)                        \
             _GLIBCXX_CLASS_REQUIRES4(_a, _b, _c, _d, __gnu_cxx, _C);
+#endif
 
 #endif // enable/disable
 


More information about the Libstdc++ mailing list