What to use as a replacement for concepts (upcoming function) in C ++?

What to use as a substitute for concepts (upcoming function) in C ++?

You may have heard about concepts in C ++. This is a function that will allow you to specify type requirements in templates.

I am looking for a way to do this now, and the best I have found is in Stroustup's Book, where he uses predicates along with static_assert as follows:

template<typename Iter, typename Val> Iter find(Iter b, Iter e, Val x) { static_assert(Input_iterator<Iter>(),"find(): Iter is not a Forward iterator"); // Rest of code... } 

Please let me know if you are using other methods or something is wrong with this.

+7
c ++ c ++ 11 c ++ - concepts
source share
3 answers

Well, a few times when I need conceptual features, I turned to "Testing the Boost Concept" . This is not the prettiest library, but it seems to have a lot of material already built-in.

The only problem I encountered is that I need to write all the attribute classes. I have not used it extensively, but probably many things have already been done for you with Boost.

+1
source share

There is a C ++ 03 method that does the part of the compilation time check provided by these concepts.

Check for a specific item

Concepts can be defined as follows ( if(0) used to suppress errors during binding. (void)test# used to suppress warnings about unused variables.):

 template <class T> struct ForwardIterator { ForwardIterator() { if(0) { void (T::* test1) () = &T::operator++; (void)test1; } } }; template <class T> struct BidirectionalIterator { BidirectionalIterator() { if(0) { ForwardIterator<T> requirement_1; void (T::* test1) () = &T::operator--; (void)test1; } } }; 

And it can be checked at compile time using a template instance:

 struct FooIterator { void operator++() {} }; template struct BidirectionalIterator<FooIterator>; 

It has the added benefit of providing compilation errors that (once you get used to them) are much better read than those provided by C ++ 11 static_assert. For example, gcc gives the following error:

 concept_test.cpp: In instantiation of 'BidirectionalIterator<T>::BidirectionalIterator() [with T = FooIterator]': concept_test.cpp:24:17: required from here concept_test.cpp:15:30: error: 'operator--' is not a member of 'FooIterator' void (T::* test1) () = &T::operator--; (void)test1; ^ 

Even MSVC2010 generates a useful compilation error, which includes what value of the template parameter T caused the error. This does not do for static_asserts.

Checking argument types and return values

If the parameters / return types depend on the class being tested, the class being tested should provide the necessary typedef. For example, the following concept checks if the class provides start and end functions that return the iterator forward:

 template <class T> struct ForwardIterable { ForwardIterable() { if(0) { ForwardIterator<typename T::Iterator> requirement_1; typename T::Iterator (T::* test1) () = &T::begin; (void)test1; typename T::Iterator (T::* test2) () = &T::end; (void)test2; } } }; 

And it is used as follows (note that typedef is required):

 struct SomeCollection { typedef FooIterator Iterator; Iterator begin(); Iterator end(); }; template struct ForwardIterable<SomeCollection>; 

Signature Verification

This method also verifies the signature extensively. In the following code, the compiler will find that the modifyFooItem argument modifyFooItem not be const.

 struct SomeFoo; template <class T> struct TestBar { TestBar() { if(0) { int (T::* test1) (SomeFoo * item) = &T::modifyFooItem; (void)test1; } } }; struct SomeBar { int modifyFooItem(const SomeFoo * item) {} }; template struct TestBar<SomeBar>; 

It generates the following error:

 concept_test.cpp: In instantiation of 'TestBar<T>::TestBar() [with T = SomeBar]': concept_test.cpp:61:17: required from here concept_test.cpp:52:47: error: cannot convert 'int (SomeBar::*)(const SomeFoo*)' to 'int (SomeBar::*)(SomeFoo*)' in initialization int (T::* test1) (SomeFoo * item) = &T::modifyFooItem; (void)test1; 
+1
source share

The best way to test concepts is to use replacement failure. However, in C ++ 98, detection using substitution substitution is rather limited. In C ++ 11, we can use replacement failures with more powerful expressions. The Tick library in C ++ 11 provides an easy way to define a concept predicate. For example, the quick and dirty is_input_iterator can be written as follows:

 TICK_TRAIT(is_input_iterator, std::is_copy_constructible<_>) { template<class I> auto requires_(I&& i) -> TICK_VALID( *i, ++i, i++, *i++ ); }; 

And then Tick also provides the TICK_REQUIRES macro to add template constraints (which just takes care of all enable_if templates), so you can simply define the function as follows:

 template<typename Iter, typename Val, TICK_REQUIRES(is_input_iterator<Iter>())> Iter find(Iter b, Iter e, Val x) { // Rest of code... } 

Ideally, you do not want to use static_assert as it generates a compilation error. Thus, we cannot determine whether find acceptable when called with certain parameters, as this will lead to a compiler error if this is not valid.

+1
source share

All Articles