问题
Given that std::array<T,N>::size
is constexpr, in the snippet below
- Why does it matter that
Foo1::u
is not astatic
member? The type is known at compile time and so is itssize()
. - 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;
};
回答1:
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.
回答2:
I recommend "resorting to" a variable to define the size in the first place:
union MyUnion {
static constexpr std::size_t size = 32;
using byte = std::uint8_t;
using word = std::uint32_t;
std::array<byte, size> bytes;
std::array<word, size / sizeof(word)> words;
};
struct Foo1 {
using Union = MyUnion;
Union u;
static constexpr std::size_t length = Union::size;
};
Why does it matter that Foo1::u is not a static member?
Non-static members can be accessed only within member functions.
I recommend using std::variant
instead of union
. It's much easier to use.
回答3:
You can directly get it from type
// direct
consteval static auto length() { return std::tuple_size<decltype(MyUnion::bytes)>::value; }
// with indirection
consteval static auto length() { return std::tuple_size<std::decay_t<decltype(u.bytes)>>::value; }
Or you can do it by create new instance.
// direct
consteval static auto length() { return MyUnion{.bytes={}}.bytes.size(); }
// just the member + indirection
consteval static auto length() { return decltype(u.bytes){}.size(); }
For the errors you got in your code
invalid use of non-static data member 'Foo1::u'
means you cannot use non-static data member u
without instance (e.g. inside static
member function).
'this' is not a constant expression*
means you cannot call this.length()
(simply because it's consteval
but this
is not here)
Why? because it's what the standard say, otherwise there is no reason to use constexpr
specifier at all because compiler can infer it, isn't it?
Related question:
- Why is constexpr not automatic?
- Why isn't std::array::size static?
来源:https://stackoverflow.com/questions/66091284/how-to-obtain-constexpr-size-of-a-non-static-stdarray-member