18

What is exactly new in c++ concepts? In my understanding they are functionally equal to using static_assert, but in a 'nice' manner meaning that compiler errors will be more readable (as Bjarne Stroustup said you won't get 10 pages or erros, but just one).

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

Is there something I am missing?

  • There is a wikipedia article about it: https://en.wikipedia.org/wiki/Concepts_(C++) – Flovdis Apr 15 '14 at 22:05
  • 1
    The [tag:c++-concepts] proposal isn't supported with the latest standard definitions AFAIR. So, what's your question about actually? (you can't use anything from concepts proposal!) – πάντα ῥεῖ Apr 15 '14 at 22:05
  • @Flovdis: The Wikipedia article doesn't quite contrast it with static_assert – Огњен Шобајић Apr 15 '14 at 22:07
  • @πάνταῥεῖ: How concepts are better than simply using static_assert? I guess they are (better) but can't seem to find where. – Огњен Шобајић Apr 15 '14 at 22:09
  • @ОгњенШобајић As I understand concepts, the STL will define some basic "concepts" like "Convertible", "LessThanComperable" etc. So you already have a base "toolkit" to set requirements for your template classes. This is far more than a static assert currently does. – Flovdis Apr 15 '14 at 22:11
  • They were refused (though Bjarne himself proposed them), so they aren't considered to be essentially better as what can be achieved with `std::static_assert` or other more sophisticated interface checks based on it. – πάντα ῥεῖ Apr 15 '14 at 22:11
  • @ОгњенШобајић You might find [this one](https://github.com/makulik/StaticInterfaces) interesting (pre c++11 stuff). – πάντα ῥεῖ Apr 15 '14 at 22:17
  • 1
    @πάνταῥεῖ, As far as I know, it was changed to just constraints because concepts wouldn't have been ready in time and concepts are still fully trying to get into the next update. – chris Apr 15 '14 at 22:18
  • @chris Yes, I also read about trying to adopt _'concepts'_ for the next standard release, but the original proposals as made by B.S. were undergoing massive critiques tending the same direction why throw specs were deprecated now IMHO. – πάντα ῥεῖ Apr 15 '14 at 22:22

1 Answers1

28

tl;dr

Compared to static_asserts, concepts are more powerful because:

  • they give you good diagnostic that you wouldn't easily achieve with static_asserts
  • they let you easily overload template functions without std::enable_if (that is impossible only with static_asserts)
  • they let you define static interfaces and reuse them without losing diagnostic (there would be the need for multiple static_asserts in each function)
  • they let you express your intents better and improve readability (which is a big issue with templates)

This can ease the worlds of:

  • templates
  • static polymorphism
  • overloading

and be the building block for interesting paradigms.


What are concepts?

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

  • allows calls to std::swap

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

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

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


Better diagnostic

As far as better diagnostic goes, we will just have to trust the committee for now. But the output they "guarantee":

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's true that you can achieve a similar output using static_asserts but that would require different static_asserts per function and that could get tedious very fast.

As an example, imagine you have to enforce the amount of requirements given by the Container concept in 2 functions taking a template parameter; you would need to replicate 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 would loose the ability to distinguish which requirement was not satisfied.

With concepts instead, you can just define the concept and enforce it by simply writing:

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

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

Concepts overloading

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

As an example you could have a function that works on Containers and overload it with a version that happens to work better with SequenceContainers:

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

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

The alternative, without concepts, would be this:

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 uglier and possibly more error prone.


Cleaner syntax

As you have seen in the examples above the syntax is definitely cleaner and more intuitive with concepts. This can reduce the amount of code required to express constraints and can improve readability.

As seen before you can actually get to an acceptable level with something like:

static_assert(Concept<T>::value);

but at that point you would loose the great diagnostic of different static_assert. With concepts you don't need this tradeoff.


Static polymorphism

And finally concepts have interesting similarities to other functional paradigms like type classes in Haskell. For example they can be used to define static interfaces.

For example, let's consider the classical approach for an (infamous) game object interface:

struct Object {
    // …
    virtual update() = 0;
    virtual draw() = 0;
    virtual ~Object();
};

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

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

Great, but unless you want to use multiple inheritance or entity-component-based systems, you are pretty much stuck with only one possible interface per class.

But if you actually want static polymorphism (polymorphism that is not that dynamic after all) you could define an Object concept that requires update and draw member functions (and possibly others).

At that point you can just create a free function:

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

And after that you could define another interface for your game objects with other requirements. The beauty of this approach is that you can develop as many interfaces as you want without

  • modifying your classes
  • require a base class

And they are all checked and enforced at compile time.

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

If you want more informations you can read this nice article on C++ concepts vs Haskell type classes.

Shoe
  • 74,840
  • 36
  • 166
  • 272
  • Thanks for the answer, but I'm not sure if re-usability, as you explained with Container is better than in static_assert. You can have a predicate e.g. Container which would tell you if the class C meets the requirements for being a Container. Then everything you need to do is static_assert::value>. Right? – Огњен Шобајић Apr 15 '14 at 22:26
  • @ОгњенШобајић, Thanks, I'll clear that up in the next revision. – Shoe Apr 15 '14 at 22:38
  • 1
    This is hell of an answer. Here I am reading it 5 years later :) Thank you again! – Огњен Шобајић Jul 20 '20 at 20:21
  • 2
    In addition, C++ concepts can be used as compile-time constant values. (btw, Concept::value is wrong, you simply need Concept). For instance : `static_assert(std::same_as);` – Guss Apr 23 '21 at 16:51
  • "classes" (not in the C++ term, but rather as a "group") should be replaced with just _categories_ – Spencer Apr 04 '23 at 15:01