Search code examples
c++constexprc++20stdarray

How to obtain constexpr `.size()` of a non-static std::array member


Given that std::array<T,N>::size is constexpr, in the snippet below

  • Why does it matter that Foo1::u is not a static member? The type is known at compile time and so is its size().
  • What's wrong with Foo2::bigger()?

Listing:

// x86-64 gcc 10.1
// -O3 --std=c++20 -pedantic -Wall -Werror

#include <array>
#include <cstdint>

union MyUnion {
    std::array<uint8_t,32> bytes;
    std::array<uint32_t,8> words;
};

struct Foo1 {
    MyUnion u;
    static constexpr size_t length {u.bytes.size()};
        //invalid use of non-static data member 'Foo1::u'
};

struct Foo2 {
    MyUnion u;
    size_t x;
    consteval int8_t length() const { return u.bytes.size(); };
    bool bigger() const { return x > length(); }
        //'this' is not a constant expression
};

I would like to keep std::array length in MyUnion declaration and not resort to

constexpr size_t LEN {32};
union MyUnion {
    std::array<uint8_t,LEN> bytes;
    std::array<uint32_t,LEN/4> words;
};

Solution

  • These situations are a bit different.

    In the first one:

        static constexpr size_t length {u.bytes.size()};
            //invalid use of non-static data member 'Foo1::u'
    

    You're trying to access a non-static data member without an object. That's just not a thing you can do. There needs to be some Foo1 associated with this expression. In the context of a non-static member function, it'd implicitly be this->u, but there still needs to be some object there. There's only one exception: you're allowed to write sizeof(Foo1::u).


    In the second one:

        consteval int8_t length() const { return u.bytes.size(); };
    

    Here, the this pointer is not itself a constant expression (we don't know what it points to), so accessing anything through it fails. This is part of a broader case of constant expressions using unknown references in a way that doesn't really affect the constant-ness of the expression (see my blog post on the constexpr array size problem). I recently wrote a paper on this topic which was focused primarily on references, but this is a narrow extension on top of that.

    Either way, for now this can't work either, because everything has to be a constant. So you'll have to resort to something along the lines of what you suggest: expose the constant you want separately.