C ++ vs static_assert concepts

What's new in C ++ concepts? In my understanding, they are functionally equal to using static_assert , but in a "good" manner, meaning that compiler errors will be more readable (since Bjarn Struustup said that you will not get 10 pages or erros, but only one).

Basically, is it true that all you can do with concepts that you can also achieve using static_assert ?

Is there something I am missing?

+8
c ++ static-assert c ++ - concepts
source share
1 answer

TL; DR

Compared to static_assert s, concepts are more efficient because:

  • they give you good diagnostics that are not easy to achieve with static_asserts
  • they allow you to easily overload template functions without std::enable_if (this is not possible only with static_asserts )
  • they allow you to define static interfaces and reuse them without losing diagnostics (several static_asserts will be needed in each function)
  • they allow you to better express your intentions and improve readability (which is a big problem for templates).

It can make worlds easier:

  • Patterns
  • static polymorphism
  • overload

and be the building block for interesting paradigms.


What are concepts?

Concepts express "classes" (not in the term C ++, but rather as a "group") of types that satisfy certain requirements. As an example, you can see that the Swappable concept expresses a set of types that:

  • lets call std::swap

And you can easily see that, for example, std::string , std::vector , std::deque , int , etc ... satisfy this requirement and therefore can be used interchangeably in a function such as:

 template<typename Swappable> void func(const Swappable& a, const Swappable& b) { std::swap(a, b); } 

Concepts have always existed in C ++ , the actual function that will be added to the (possibly near) future will simply allow you to express and enforce them in the language.


Improved Diagnostics

As for the best diagnosis, we just have to trust the committee at the moment. But they "guarantee" the way out:

 error: no matching function for call to 'sort(list<int>&)' sort(l); ^ note: template constraints not satisfied because note: `T' is not a/an `Sortable' type [with T = list<int>] since note: `declval<T>()[n]' is not valid syntax 

is very promising.

It is true that you can achieve similar output with static_assert , but this will require a different static_assert for each function, and this can become tedious very quickly.

As an example, suppose that you must enforce the requirements outlined in the Container concept in 2 functions that accept a template parameter; you will need to play them in both functions:

 template<typename C> void func_a(...) { static_assert(...); static_assert(...); // ... } template<typename C> void func_b(...) { static_assert(...); static_assert(...); // ... } 

Otherwise, you will lose the ability to distinguish which requirement has not been met.

Instead of concepts, you can simply define a concept and apply it simply by writing:

 template<Container C> void func_a(...); template<Container C> void func_b(...); 

Concept overload

Another great feature that is introduced is the ability to overload template functions with template restrictions. Yes, this is also possible with std::enable_if , but we all know how ugly this can be.

As an example, you might have a function that runs on Container and overloads with a version that works better with SequenceContainer s:

 template<Container C> int func(C& c); template<SequenceContainer C> int func(C& c); 

An alternative, without concepts, would be the following:

 template<typename T> std::enable_if< Container<T>::value, int > func(T& c); template<typename T> std::enable_if< SequenceContainer<T>::value, int > func(T& c); 

Definitely more ugly and possibly more error prone.


Syntax cleaner

As you saw in the examples above, the syntax is definitely cleaner and more intuitive to concepts. This can reduce the amount of code needed to express constraints, and can improve readability.

As you can see, you can really go to an acceptable level with something like:

 static_assert(Concept<T>::value); 

but at this point you will lose the excellent diagnosis of various static_assert . With concepts you do not need this compromise.


Static polymorphism

And finally, the concepts have an interesting resemblance to other functional type-type paradigms in Haskell. For example, they can be used to define static interfaces.

For example, consider the classic approach for a (infamous) game object interface:

 struct Object { // โ€ฆ virtual update() = 0; virtual draw() = 0; virtual ~Object(); }; 

Then, assuming you have a polymorphic std::vector derived objects, you can do:

 for (auto& o : objects) { o.update(); o.draw(); } 

Great, but if you don't want to use multiple inheritance systems or entities, you are practically stuck with only one possible interface for each class.

But if you really want static polymorphism (a polymorphism that is not such dynamics after all), you can define an Object concept that requires update and draw member functions (and possibly others).

At this point, you can simply create a free function:

 template<Object O> void process(O& o) { o.update(); o.draw(); } 

And after that you can define a different interface for your game objects with different requirements. The beauty of this approach is that you can develop as many interfaces as you want,

  • class change
  • base class required

And they are all checked and applied at compile time.

This is just a silly example (and very simplistic), but the concepts really open up a whole new world for templates in C ++.

If you need more information, you can read this nice article about C ++ concepts and classes like Haskell.

+16
source share

All Articles