Semantic requirements in concepts

The word ‘concept’ in the context of C++ generic programming has two meanings. The first is more abstract: it is the notion from the domain of Generic Programming (GP) in general. GP is not tied to any specific language: it is an approach to writing programs, and concepts are part of this approach. In this sense concepts have been with us since the inception of the STL. The second meaning is the keyword concept in C++20 with its associated semantics: its goal is to approximate the more abstract notion of a concept from GP, and this works only to some extent. One notable difference is that concepts in GP specify semantic requirements on types they constrain, and C++ concepts cannot express them directly.

In this post we will see how semantic requirements in concepts can break your program if you don’t pay attention to them, and what can be done in C++20 concepts to account for semantic requirements. Continue reading

Posted in programming | Tagged , , , | 7 Comments

Reflection for aggregates

An aggregate is an array or a class with

  • no user-declared or inherited constructors,
  • no private or protected direct non-static data members,
  • no virtual functions, and
  • no virtual, private, or protected base classes.

Aggregates can be initialized in aggregate initialization, and, for most cases, decomposed in a structured binding:

struct Point { int x, y; }; // aggregate

Point pt = {1, 2};          // aggregate init
auto const& [x, y] = pt;    // decomposition

Aggregate classes (that is, not arrays) in some aspects are close to tuples, except that their members have meaningful names. However, unlike tuples, you cannot access their members by index.

In this post we will see how to provide an index-based access to aggregate members and write a “for-each” loop that iterates over all members of a given aggregate type. We will also have a look at PFR (Precise and Flat Reflection) library which implements this already. Continue reading

Posted in programming | Tagged , , | Leave a comment

Concept archetypes — update

An observant reader indicated that in the previous post where I was trying to implement a concept archetype — a type with minimal interface that models a given concept — I actually failed. This deserves a closer examination. Continue reading

Posted in programming | Tagged , , | 6 Comments

Concept archetypes

Concepts in the form added in C++20 used to be called lite. This is because they do not provide one quite important functionality: having the compiler check if the author of a constrained template is only using operations and types allowed by the constraining concept. In other words, we can say that our template only requires operations A and B to be valid, but we can still use some other operations inside and this is fine with the compiler. In this post we will show how this is problematic, even for programmers aware of the issue, and how to address it with concept archetypes. Continue reading

Posted in programming | Tagged , , | 13 Comments

Ordering by constraints

In the previous post we have seen how constraint conjunction and disjunction works, and how a function template with constraints is a better match than a function template without constraints (provided that the constraints are satisfied) when determining the best overload. We have also mentioned that selecting a better match from two constrained templates is possible, but not obvious. In this post we will expand on this, and show how constraint conjunction and disjunction as well as concepts play an important role in ordering function overloads and class template specializations based solely on constraints. This is one of the situations where language concepts show their special properties. Continue reading

Posted in programming | Tagged , , , | 4 Comments

Requires-clause — updated

The previous post, “Requires-clause”, contained incorrect information about parentheses inside a requires-clause. Token || inside parentheses is still interpretted as a disjunction of two constraints. I apologize for misleading the readers. I also want to thank James Pfeffer for bringing this error to my attention. The post has now been corrected. You might wish to re-read section Conjunction and Disjunction.

Posted in programming | Tagged , , , | 2 Comments

Requires-clause

Update. This post in its original form contained incorrect information about the meaning of parentheses inside requires-clauses in section Conjunction and Disjunction. The section has now been changed to correct this. The updated text is in blueish color. Even if you have already read this post, I encourage you to read the section again. I am sorry for having misinformed the readers in the original text. I also want to thank James Pfeffer for pointing out this error.

In this post we will talk about another C++20 feature related to constraining templates: requires-clause. Although C++20 is due to be published this year, it is not there yet; so we are talking about the future. However, this can already be tested in trunk versions of GCC and Clang online in Compiler Explorer. A requires-clause looks like this:

template <typename T>
  requires is_standard_layout_v<T> && is_trivial_v<T>
void fun(T v); 

int main()
{
  std::string s;

  fun(1);  // ok
  fun(s);  // compiler error
}

It is an additional “clause” in a template declaration that expresses under what condition the constrained template is supposed to work. It looks like this is an ordinary Boolean expression that gives a yes-no answer, but it is not quite so. If we try to negate one of the operands in the declaration:

template <typename T>
  requires is_standard_layout_v<T> && !is_trivial_v<T>
void fun(T v); 

The declaration is no longer valid, and the program fails to compile. So, there is more to the story. I assume that you are already familiar with C++20 concepts, at least superficially. In this post we will explore requires-clause in more detail. Continue reading

Posted in programming | Tagged , , , | 6 Comments

Requires-expression

This post is about a C++20 feature, so we will be talking about the future. However, this is a very near feature, C++20 is expected to go out this year, and concepts look really stable, so the chances are high that they will become standard pretty soon. Meanwhile, two experimental implementations can be tested online in Compiler Explorer. I assume that you are already familiar, at least superficially, with C++20 concepts. In this post we will explore only one part of it: requires-expressions.

This is how a concept declaration would often look like:

template <typename T>
concept Machine = 
  requires(T m) {  // any `m` that is a Machine
    m.start();     // must have `m.start()` 
    m.stop();      // and `m.stop()`
  };

But this is in fact two separate features that happen to work together. One is a concept, the other is a requires-expression. We can declare a concept without a requires-expression:

template <typename T>
concept POD = 
  std::is_trivial<T>::value &&
  std::is_standard_layout<T>::value;

We can also use a requires-expression for other purposes than declaring a concept:

template <typename T>
void clever_swap(T& a, T& b)
{
  constexpr bool has_member_swap = requires(T a, T b){ 
    a.swap(b); 
  };

  if constexpr (has_member_swap) {
    a.swap(b);
  }
  else {
    using std::swap;
    swap(a, b);
  }
}

In this post we will look at requires-expression as a stand-alone feature, and explore its limits. Continue reading

Posted in programming | Tagged , | 18 Comments

Short-circuiting in meta-functions

Short-circuiting in logical operations is a very useful and an often used feature:

if (cond_a() && cond_b())

Should cond_a() evaluate to false, cond_b() is guaranteed not to be evaluated. This is useful for two reasons. One is performance: if cond_b() is an expensive operation we do not want to evaluate it if we can determine the final result from only evaluating cond_a(). The other reason is program correctness:

if (ptr && ptr->is_ready())

Here the first operand is a precondition for the second operand, and we do not want the latter to be evaluated if the former isn’t true. Short-circuiting makes this work as desired.

For similar reasons, we might want to use short-circuiting of logical operations in meta-functions. However, in meta-functions this looks and works differently. This is what this post is about. Continue reading

Posted in programming | Tagged , , , | 4 Comments