Let’s try C++20 | std::cmp_* functions

Today, I have discovered a very simple (but yet very efficient and useful) feature from C++20: the std::cmp_* functions. They allow you to compare 2 integers, which can have different signedness, without getting bitten by implicit conversions.

Let’s have a look at this piece of code:

#include <iostream>

int main()
{
    int a = -6;
    unsigned int b = 3;
    
    if (a < b) {
        std::cout << "OK: a is lower than b\n";
    } else {
        std::cout << "Oopsi!\n";
    }
}

Yes, you’ve guessed right: it prints “Oopsi!”.

Forbid a particular specialization of a template

Let’s imagine a simple template function that performs basic numerical computations:

template <typename T>
T f(T t) {
    return 2 * t + 3;
}

Nothing seems wrong with this function. You can use it on several types: f(15) returns 33 and T is int, f(2.3) returns 7.6 and T is double, etc. If T is not a type that supports addition and multiplication, you get a compiler error. Example with f("hello"):

error: invalid operands to binary expression ('int' and 'const char *')

Let’s try C++20 | std::is_constant_evaluated()

std::is_constant_evaluated() is one of the many new small features of C++20. Specified in P0595R2, this function is available in the <type_traits> header. Here is its description from cppreference:

constexpr bool is_constant_evaluated() noexcept;

Detects whether the function call occurs within a constant-evaluated context. Returns true if the evaluation of the call occurs within the evaluation of an expression or conversion that is manifestly constant-evaluated; otherwise returns false.

This sounds pretty straightforward.

Benefits for constexpr functions

This may be very interesting for constexpr functions. You probably know that you have no guarantee that a constexpr function is really executed at compile-time. This depends on how the function is called:

Let’s try C++20 | using enum

Proposal P1099R5 by Gašper Ažman and Jonathan Müller adds the possibility to write using enum my_enum; so that the names inside the enumeration my_enum can be used directly, without being preceded by my_enum::.

Let’s consider an enumeration like this:

enum class EnumWithALongName {
    FirstValue,
    SecondValue,
    YetAnotherValue
};

Until C++17, you had to write their fully qualified names to use the enumerators. Sometimes, it would make code quite verbose:

void process(EnumWithALongName value) {
    switch(value) {
        case EnumWithALongName:::FirstValue:
            // stuff
        case EnumWithALongName::SecondValue:
            // more stuff
        case EnumWithALongName::YetAnotherValue:
            // OK, I got it, we are dealing with EnumWithALongName...
    }
}

Thanks to C++20, we can now write:

Let’s try C++20 | explicit(bool)

From the many changes of C++20, let’s focus today on explicit(bool). This feature is described in P0892R2. The explicit keyword avoids implicit conversions by unexpectedly calling a constructor. With C++20, it is now possible to have a boolean condition as a “parameter” to the keyword.

How it works

This simple code makes it easy to understand how explicit(bool) works:

constexpr bool ENABLE_EXPLICIT = false;

struct Foo {
    explicit(ENABLE_EXPLICIT)
    Foo(int) {}
};

Foo a = 1;

This code compiles fine, but change ENABLE_EXPLICIT to true and you will get an error with clang-10.0.0: