9

I'm working with a variable-width communications format. The structs to handle it look something like this:

struct Header
{
  int msgType = -1, len;

  Header() { len = sizeof(*this); }
};

struct A : public Header
{
  int x; char y;

  A() { msgType = 1; len = sizeof(*this); }
};

// Further structs B, C, ... declared along the same lines

I would like to have a constexpr static member Header::MAX_SIZE which gives the max size of any of these derived classes, e.g. so I can allocate a buffer which is guaranteed to hold any such packet. So I'd like to do something like

struct Header
{
  int msgType = -1, len;

  constexpr static std::size_t MAX_SIZE;

  Header() { len = sizeof(*this); }
};

// ... declaration of subclasses ...

inline Header::MAX_SIZE = std::max({ sizeof(A), sizeof(B), sizeof(C) });

I need the definition to come outside of the class because it depends on sizeof(A), etc., which in turn depend on the definition of Header.

It seems like this sort of thing should be unobjectionable: I'm giving the definition of the member in the same source file, and it can be computed at compile time. But I haven't found any way to tell the compiler to actually do this.

Daniel McLaury
  • 4,047
  • 1
  • 15
  • 37
  • You can do this readily if you'll settle for `const` rather than `constexpr`. Or is that just not good enough? – Adrian Mole Apr 28 '21 at 12:49
  • I wanted to use it at compile time. I guess I can just declare this at namespace scope instead of inside the class, but this seems unnecessary. – Daniel McLaury Apr 28 '21 at 12:50
  • Hmmm. I think a compile-time (constexpr) member of a base class whose value depends on the definitions of derived classes is opening the door to all sorts of recursive circularities. C++20 may have something that addresses this ... I haven't got round to fully digesting that Standard, yet. – Adrian Mole Apr 28 '21 at 12:56
  • I suppose you'd have to disallow using it from code in between the declaration and the definition. – Daniel McLaury Apr 28 '21 at 12:57
  • Related: https://stackoverflow.com/q/11928089/103167 – Ben Voigt Apr 28 '21 at 17:03

1 Answers1

8

constexpr goes on the initializing declaration of a variable, so just put it outside the class:

struct Header
{
  int msgType = -1, len;

  static const std::size_t MAX_SIZE;

  Header() { len = sizeof(*this); }
};

// ... declaration of subclasses ...

inline constexpr std::size_t Header::MAX_SIZE = std::max({ sizeof(A), sizeof(B), sizeof(C) });

Note that the implicit const must be spelled out in the declaration. The definition should go in the same header to avoid any translation unit seeing the declaration but not the inline, which is not allowed.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • 1
    Do you really need the `inline` keyword here? – xuhdev Apr 06 '22 at 19:46
  • 2
    @xuhdev: The *intent* is to require it since not doing so is a [regression versus C++14](https://cplusplus.github.io/CWG/issues/2531.html). – Davis Herring Apr 06 '22 at 20:10
  • Do you have a reference that your code snippet is equivalent to initializing a `constexpr` static member inside the `struct` definition? I found this example https://en.cppreference.com/w/cpp/language/static (second last code block) and it looks a bit concerning that initializing a `constexpr` data member may not be allowed outside of the class definition. – xuhdev Apr 06 '22 at 20:22
  • @xuhdev: I don’t understand: it’s declared `constexpr` and is initialized on *that* declaration. What inequivalence are you supposing might exist? – Davis Herring Apr 06 '22 at 20:53
  • I'm Sorry I said it in the wrong way. What I meant is that, would you be sure that your code snippet is standard compliant? I couldn't find any sources that say defining a `constexpr` data member can be done via declaring `const` in class and initializing with `constexpr` out of class. – xuhdev Apr 06 '22 at 21:06
  • @xuhdev: I am sure, but it’s hard to give a citation since the standard doesn’t enumerate every valid sequence of declarations (and it’s not been widely used, as evidenced by the fact that we’re just now finding the bug in C++17). – Davis Herring Apr 06 '22 at 22:52
  • 1
    @xuhdev Here's a godbolt example showing it working as intended: https://godbolt.org/z/zrEjf9cvE – Spencer Mar 13 '23 at 20:01