Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are constexpr containers in C++17?

I want to make a class that has methods like std::map, but it should be sorted at compile time. Which constexpr containers are suited to store keys template<class K> and values template<class V>?

std::vector does not meet these requirements.

UPD: We found that std::array has a lot of constexpr methods. And it's enough for my problem to use std::array<std::pair<K, V> >. But the question holds.

like image 378
InFamous X Avatar asked Mar 18 '18 21:03

InFamous X


People also ask

What is constexpr used for?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.

What is the difference between const and constexpr?

The principal difference between const and constexpr is the time when their initialization values are known (evaluated). While the values of const variables can be evaluated at both compile time and runtime, constexpr are always evaluated at compile time.

Is constexpr same as #define?

#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.

What does static constexpr mean?

static defines the object's lifetime during execution; constexpr specifies that the object should be available during compilation. Compilation and execution are disjoint and discontiguous, both in time and space. So once the program is compiled, constexpr is no longer relevant.


1 Answers

Most C++ standard library containers are not useful as constexpr. AFAIK only first 64 bits of std::bitset and std::array (of any length) are fillable compile time.

Do not concentrate on such performance optimizations before the program itself is ready. Filling large std::array compile-time was not too hard in C++11 using variadic template functions.

Example of how to fill array of 4 ints (each representing one of 6 colors) compile time using variadic templates:

constexpr int ColorCount = 6;
constexpr int PositionCount = 4;

using Positions = std::array<int, PositionCount>;

template <int N>
struct PositionsFiller
{
    template <typename T, typename ...Tn>
    static constexpr Positions fill(T packed, Tn ...rest)
    {
        return PositionsFiller<N - 1>::fill(packed / ColorCount, packed % ColorCount, rest...);
    }
};

template <>
struct PositionsFiller<1>
{
    template <typename T, typename ...Tn>
    static constexpr Positions fill(T last, Tn ...rest)
    {
        return Positions{last, rest...};
    }
};

constexpr Positions pos666(PositionsFiller<PositionCount>::fill(666));

In C++17 same can be done with simple loop since requirements to constexpr are relaxed and there variadic templates are not needed:

constexpr int ColorCount = 6;
constexpr int PositionCount = 4;

using Positions = std::array<int, PositionCount>;

static constexpr Positions fillPositions(int packed)
{
    Positions ret{};
    for (Positions::size_type i = ret.size(); i > 0; --i)
    {
        ret[i-1] = packed % ColorCount;
        packed /= ColorCount;
    }
    return ret;
}

constexpr Positions pos666(fillPositions(666));

Note that doing complex preparations compile time can slow down compilations. That may be annoying when module is still under development. Better can be to fill usual mutable array at start of program and later to replace it with optimizations like compile-time filling.

like image 192
Öö Tiib Avatar answered Oct 10 '22 10:10

Öö Tiib