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 , , , | 14 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 , | 31 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

String literals make bad ranges

C++20 will come with what we call “Ranges” library. Meanwhile, range interface has been supported ever since C++11 in one context: range-based for loop. A range-based for loop can detect anything that is a range and work with it. In particular, it can work with string literals:

int main()
{
  for (char ch : "ABC")
    std::cout << ch << "\n";
}

If you test this program, it looks like it displays what you think: A, B, and C in a column; but in fact, what it does is slightly different than what one would intuitively assume. Continue reading

Posted in programming | Tagged , , , | 2 Comments

Operation cancelling and std::fstream

In the previous post we have sketched out the view that error handling is about expressing the success dependency between operations. I have also indicated the guideline “destructors only for releasing resources”. In this post we are going to see what it means in practice. We will try to save some data to a file using std::ofstream. Continue reading

Posted in programming | Tagged , , , | 11 Comments

Handling errors is canceling operations

I actually covered this topic before, in this post, but given my recent experience I feel it needs reiterating and a bit of restructuring. It boils down to the observation that any error handling I have encountered — be it error codes, errno, exceptions, error monad — is about canceling operations that depend, directly or indirectly, on the function that reported failure. This has some consequences on how we look at our program flow and what principles we should follow when responding to failures in our programs. Continue reading

Posted in programming | Tagged , , , | 6 Comments

(Not) using namespace std;

using namespace std;

This is called a using directive. Using it in code that is supposed to work for years is not a good idea.

Sometimes programmers are given advice to just type using namespace std; and all things from the Standard Library can be used easily, without bothering with namespaces. This is a good advice when you are learning C++ and when you are doing toy examples, or write small private programs. Because for these programs it is not a big deal if they cease to compile once you change the compiler or language version. Continue reading

Posted in programming | Tagged | 12 Comments

Deducing your intentions

The language feature in C++17 known as class template argument deduction was intended to supersede factory functions like make_pair, make_tuple, make_optional, as described in p0091r2. This goal has not been fully achieved and we may still need to stick to make_ functions. In this post we will briefly describe what class template argument deduction is, and why it works differently than what people often expect. Continue reading

Posted in programming | Tagged , , | 12 Comments

Treating symptoms instead of the cause

Compiler warnings can sometimes help us find bugs before we even build our program, but it only works provided that we can make use of the warnings. Especially, when we are aware that warnings detect only symptoms of the bugs rather than bugs themselves. Continue reading

Posted in programming | Tagged , , | 8 Comments