Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

static constexpr in a class

1,493 views
Skip to first unread message

David Brown

unread,
Apr 20, 2019, 8:43:28 AM4/20/19
to
I was recently working on a class where I wanted a number of bits and
pieces calculated at compile time, using members of the class. This is
(obviously) not the actual code, but it shows the same effect:

class A {
static constexpr int next(int x) { return x + 1; }
static constexpr const int one = next(0);
};


It seems that it is illegal to initialise the constexpr object "one"
using the function "next", because the definition of "next" is not
considered complete until the end of class A.

It is fine to have these two definitions outside a class. It is also
fine to write:

class A {
static constexpr int next(int x) { return x + 1; }
static const int one;
};
constexpr const int A::one = next(0);


Does anyone know any reason for why the first version within the class
is not allowed?

Are there any other ways to get the same results? The second version
works fine, but it splits up the definitions in a way that didn't fit
the flow of the code I was writing.

Alf P. Steinbach

unread,
Apr 20, 2019, 8:53:53 AM4/20/19
to
First, consider using `std::next` ;-)

nonstd::next, re the problem, consider having at namespace scope

constexpr int next(int x);
constexpr const int one = next(0);

constexpr int next(int x) { return x + 1; }

This reproduces the problem with ordinary functions. It's invalid code.

So I guess that even the rules for `constexpr` members effectively
implement the conceptual view that in-class-definition class member
function bodies are compiled as if they were moved to after the class.

It's a nice view, it explains most everything that can baffle a novice
about in-class-definition member functions. I didn't know that it also
explains `constexpr` member behavior. Thanks.


Cheers!,

- Alf

David Brown

unread,
Apr 20, 2019, 10:05:13 AM4/20/19
to
On 20/04/2019 14:53, Alf P. Steinbach wrote:
> On 20.04.2019 14:43, David Brown wrote:
>> I was recently working on a class where I wanted a number of bits and
>> pieces calculated at compile time, using members of the class.  This
>> is (obviously) not the actual code, but it shows the same effect:
>>
>> class A {
>>      static constexpr int next(int x) { return x + 1; }
>>      static constexpr const int one = next(0);
>> };
>>
>>
>> It seems that it is illegal to initialise the constexpr object "one"
>> using the function "next", because the definition of "next" is not
>> considered complete until the end of class A.
>>
>> It is fine to have these two definitions outside a class.  It is also
>> fine to write:
>>
>> class A {
>>      static constexpr int next(int x) { return x + 1; }
>>      static const int one;
>> };
>> constexpr const int A::one = next(0);
>>
>>
>> Does anyone know any reason for why the first version within the class
>> is not allowed?
>>
>> Are there any other ways to get the same results?  The second version
>> works fine, but it splits up the definitions in a way that didn't fit
>> the flow of the code I was writing.
>
> First, consider using `std::next` ;-)

It's getting steadily harder to pick names that don't coincide with the
standard library! It's just an example function name - no relation to
std::next.

>
> nonstd::next, re the problem, consider having at namespace scope
>
>     constexpr int next(int x);
>     constexpr const int one = next(0);
>
>     constexpr int next(int x) { return x + 1; }
>
> This reproduces the problem with ordinary functions. It's invalid code.
>

Yes, that is because you are trying to use the constexpr function (to
initialise "one") before it is defined.

> So I guess that even the rules for `constexpr` members effectively
> implement the conceptual view that in-class-definition class member
> function bodies are compiled as if they were moved to after the class.
>
> It's a nice view, it explains most everything that can baffle a novice
> about in-class-definition member functions. I didn't know that it also
> explains `constexpr` member behavior. Thanks.
>

That sounds like a way to understand the rules.

In my case, the constexpr is actually fully defined before it is used -
but because it is in a class that is not yet completed, the function
definition is not considered complete either.

I can appreciate that that this might not be the case. In particular,
functions that depend on the class itself could not be considered "fully
defined" until the class is fully defined - data members could be added
after the definition, for example.

So perhaps the rules of when the functions can be used are made to give
a simple and consistent view, rather than a more complex set of rules
about what is and is not allowed.

Alf P. Steinbach

unread,
Apr 20, 2019, 10:43:02 AM4/20/19
to
I don't buy that as a technical explanation, and I don't think it's a
good conceptual view (because it doesn't explain anything else, it's
just a special case arbitrary rule).

The ordinary conceptual view of a compiler's rewrite where it moves the
member function definitions to after the class, fits too well.

That is, it fits everything I know.


> I can appreciate that that this might not be the case.  In particular,
> functions that depend on the class itself could not be considered "fully
> defined" until the class is fully defined - data members could be added
> after the definition, for example.
>
> So perhaps the rules of when the functions can be used are made to give
> a simple and consistent view, rather than a more complex set of rules
> about what is and is not allowed.

Yes.


Cheers!,

- Alf

blt_4...@um7ygq6ykv2ca0gs.edu

unread,
Apr 20, 2019, 11:35:59 AM4/20/19
to
On Sat, 20 Apr 2019 14:43:16 +0200
David Brown <david...@hesbynett.no> wrote:
>I was recently working on a class where I wanted a number of bits and
>pieces calculated at compile time, using members of the class. This is
>(obviously) not the actual code, but it shows the same effect:
>
>class A {
> static constexpr int next(int x) { return x + 1; }
> static constexpr const int one = next(0);
>};
>
>
>It seems that it is illegal to initialise the constexpr object "one"
>using the function "next", because the definition of "next" is not
>considered complete until the end of class A.
>
>It is fine to have these two definitions outside a class. It is also
>fine to write:
>
>class A {
> static constexpr int next(int x) { return x + 1; }
> static const int one;
>};
>constexpr const int A::one = next(0);

I don't really see the point of these syntatic games that constexpr allows.
If its a constant expression then by definition you know the value when you
write the code so why not just have "static const int one = 1"?

David Brown

unread,
Apr 21, 2019, 4:03:10 AM4/21/19
to
Obviously the real functions here are not as simple as "x + 1".

blt__k...@dmq3k.co.uk

unread,
Apr 21, 2019, 6:16:03 AM4/21/19
to
On Sun, 21 Apr 2019 10:03:01 +0200
They're never going to contain anything more than simple arithmetic formulas
if the output can be computed at compile time so why not just make them a
const.

David Brown

unread,
Apr 21, 2019, 7:34:36 AM4/21/19
to
/I/ am the person writing these functions here. /I/ know they are not
simple arithmetic. The result of some of the functions is a single
integer, others result in arrays of data, but the functions involved are
not simple. There are good reasons why I am using functions, not just
doing the arithmetic in my head, and there are good reasons why I want
them to be constexpr.

blt...@zimftew_1xvn.com

unread,
Apr 22, 2019, 4:31:17 AM4/22/19
to
On Sun, 21 Apr 2019 13:34:25 +0200
You must have a magic compiler that can execute loops, call other functions and
execute other non arithmetic and boolean logic constructs at compile time then.

>integer, others result in arrays of data, but the functions involved are
>not simple. There are good reasons why I am using functions, not just

Give us an example of these complex functions that can still be reduced to
a single value at compile time.

David Brown

unread,
Apr 22, 2019, 5:33:57 AM4/22/19
to
It's a C++14 compiler - there are several available. C++14 removed many
of the restrictions on C++11 constexpr functions.

<https://en.cppreference.com/w/cpp/language/constexpr>

>> integer, others result in arrays of data, but the functions involved are
>> not simple. There are good reasons why I am using functions, not just
>
> Give us an example of these complex functions that can still be reduced to
> a single value at compile time.
>

The code below is originally from C, rather than C++, simply augmented
by "constexpr". Even with optimisation disabled, a C++14 compiler must
calculate the CRC check here at compile time.


#include <stdint.h>
#include <string.h>

static constexpr const uint8_t crcTable8[256] = {
0x00, 0x8D, 0x97, 0x1A, 0xA3, 0x2E, 0x34, 0xB9,
0xCB, 0x46, 0x5C, 0xD1, 0x68, 0xE5, 0xFF, 0x72,
0x1B, 0x96, 0x8C, 0x01, 0xB8, 0x35, 0x2F, 0xA2,
0xD0, 0x5D, 0x47, 0xCA, 0x73, 0xFE, 0xE4, 0x69,
0x36, 0xBB, 0xA1, 0x2C, 0x95, 0x18, 0x02, 0x8F,
0xFD, 0x70, 0x6A, 0xE7, 0x5E, 0xD3, 0xC9, 0x44,
0x2D, 0xA0, 0xBA, 0x37, 0x8E, 0x03, 0x19, 0x94,
0xE6, 0x6B, 0x71, 0xFC, 0x45, 0xC8, 0xD2, 0x5F,
0x6C, 0xE1, 0xFB, 0x76, 0xCF, 0x42, 0x58, 0xD5,
0xA7, 0x2A, 0x30, 0xBD, 0x04, 0x89, 0x93, 0x1E,
0x77, 0xFA, 0xE0, 0x6D, 0xD4, 0x59, 0x43, 0xCE,
0xBC, 0x31, 0x2B, 0xA6, 0x1F, 0x92, 0x88, 0x05,
0x5A, 0xD7, 0xCD, 0x40, 0xF9, 0x74, 0x6E, 0xE3,
0x91, 0x1C, 0x06, 0x8B, 0x32, 0xBF, 0xA5, 0x28,
0x41, 0xCC, 0xD6, 0x5B, 0xE2, 0x6F, 0x75, 0xF8,
0x8A, 0x07, 0x1D, 0x90, 0x29, 0xA4, 0xBE, 0x33,
0xD8, 0x55, 0x4F, 0xC2, 0x7B, 0xF6, 0xEC, 0x61,
0x13, 0x9E, 0x84, 0x09, 0xB0, 0x3D, 0x27, 0xAA,
0xC3, 0x4E, 0x54, 0xD9, 0x60, 0xED, 0xF7, 0x7A,
0x08, 0x85, 0x9F, 0x12, 0xAB, 0x26, 0x3C, 0xB1,
0xEE, 0x63, 0x79, 0xF4, 0x4D, 0xC0, 0xDA, 0x57,
0x25, 0xA8, 0xB2, 0x3F, 0x86, 0x0B, 0x11, 0x9C,
0xF5, 0x78, 0x62, 0xEF, 0x56, 0xDB, 0xC1, 0x4C,
0x3E, 0xB3, 0xA9, 0x24, 0x9D, 0x10, 0x0A, 0x87,
0xB4, 0x39, 0x23, 0xAE, 0x17, 0x9A, 0x80, 0x0D,
0x7F, 0xF2, 0xE8, 0x65, 0xDC, 0x51, 0x4B, 0xC6,
0xAF, 0x22, 0x38, 0xB5, 0x0C, 0x81, 0x9B, 0x16,
0x64, 0xE9, 0xF3, 0x7E, 0xC7, 0x4A, 0x50, 0xDD,
0x82, 0x0F, 0x15, 0x98, 0x21, 0xAC, 0xB6, 0x3B,
0x49, 0xC4, 0xDE, 0x53, 0xEA, 0x67, 0x7D, 0xF0,
0x99, 0x14, 0x0E, 0x83, 0x3A, 0xB7, 0xAD, 0x20,
0x52, 0xDF, 0xC5, 0x48, 0xF1, 0x7C, 0x66, 0xEB
};

static constexpr uint8_t crcData8(uint8_t crc, const uint8_t* pData,
size_t size) {
while (size--) {
crc = crcTable8[crc ^ (*pData++)];
}
return crc;
}

uint8_t test(void) {
const uint8_t s[] = "Hello, world!";
constexpr uint8_t c = crcData8(0, s, sizeof(s));
return c;
}

blt_q7...@0gkdzjla5uk04q8c.com

unread,
Apr 22, 2019, 5:53:43 AM4/22/19
to
On Mon, 22 Apr 2019 11:33:48 +0200
David Brown <david...@hesbynett.no> wrote:
>On 22/04/2019 10:31, blt_7W@zimftew_1xvn.com wrote:
>> Give us an example of these complex functions that can still be reduced to
>> a single value at compile time.
>>
>
>The code below is originally from C, rather than C++, simply augmented
>by "constexpr". Even with optimisation disabled, a C++14 compiler must
>calculate the CRC check here at compile time.

Or not even in 2017 mode....

fenris$ c++ -v
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.5.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
fenris$ c++ -std=c++17 test.cc
test.cc:49:24: error: constexpr variable 'c' must be initialized by a constant
expression
constexpr uint8_t c = crcData8(0, s, sizeof(s));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~
test.cc:42:40: note: read of non-constexpr variable 's' is not allowed in a
constant expression
crc = crcTable8[crc ^ (*pData++)];
^
test.cc:49:28: note: in call to 'crcData8(0, &s[1], 13)'
constexpr uint8_t c = crcData8(0, s, sizeof(s));
^
1 error generated.

Whoops.

Ian Collins

unread,
Apr 22, 2019, 6:05:42 AM4/22/19
to
On 22/04/2019 21:53, blt_q7...@0gkdzjla5uk04q8c.com wrote:
> On Mon, 22 Apr 2019 11:33:48 +0200
> David Brown <david...@hesbynett.no> wrote:
>> On 22/04/2019 10:31, blt_7W@zimftew_1xvn.com wrote:
>>> Give us an example of these complex functions that can still be reduced to
>>> a single value at compile time.
>>>
>>
>> The code below is originally from C, rather than C++, simply augmented
>> by "constexpr". Even with optimisation disabled, a C++14 compiler must
>> calculate the CRC check here at compile time.
>
> Or not even in 2017 mode....

It is if you fix the spello:

constexpr uint8_t s[] = "Hello, world!";

gcc is a bit lenient on the original.

--
Ian.


David Brown

unread,
Apr 22, 2019, 6:14:54 AM4/22/19
to
So it is - even in pedantic mode. I guess that could be filed as a bug.

(clang will, of course, do everything at compile time with "s" being
"const", as long as "c" is also changed to "const" instead of
"constexpr". But it then does so for optimisation, not because the
rules of C++ require it.)

blt_zw...@mlz0jr60in9.ac.uk

unread,
Apr 22, 2019, 10:42:15 AM4/22/19
to
Hmm, thats interesting. Guess I should have caught up with 2014 constexprs.
I can't help thinking that the compiler executing code at compile time could
be an interesting avenue for obscure bugs and compile time crashes.

David Brown

unread,
Apr 22, 2019, 11:05:37 AM4/22/19
to
Obscure bugs, yes. Compile time crashes are just compile-time error
messages.

There are still restrictions on what you can do in constexpr functions,
of course. But they are a lot less than in C++11.

blt...@zz6h0uww529.com

unread,
Apr 22, 2019, 11:26:37 AM4/22/19
to
On Mon, 22 Apr 2019 17:05:27 +0200
David Brown <david...@hesbynett.no> wrote:
>On 22/04/2019 16:42, blt_zw...@mlz0jr60in9.ac.uk wrote:
>> On Mon, 22 Apr 2019 22:05:32 +1200
>> Ian Collins <ian-...@hotmail.com> wrote:
>>> On 22/04/2019 21:53, blt_q7...@0gkdzjla5uk04q8c.com wrote:
>>>> On Mon, 22 Apr 2019 11:33:48 +0200
>>>> David Brown <david...@hesbynett.no> wrote:
>>>>> On 22/04/2019 10:31, blt_7W@zimftew_1xvn.com wrote:
>>>>>> Give us an example of these complex functions that can still be reduced
>to
>>>>>> a single value at compile time.
>>>>>>
>>>>>
>>>>> The code below is originally from C, rather than C++, simply augmented
>>>>> by "constexpr". Even with optimisation disabled, a C++14 compiler must
>>>>> calculate the CRC check here at compile time.
>>>>
>>>> Or not even in 2017 mode....
>>>
>>> It is if you fix the spello:
>>>
>>> constexpr uint8_t s[] = "Hello, world!";
>>>
>>
>> Hmm, thats interesting. Guess I should have caught up with 2014 constexprs.
>> I can't help thinking that the compiler executing code at compile time could
>> be an interesting avenue for obscure bugs and compile time crashes.
>>
>
>Obscure bugs, yes. Compile time crashes are just compile-time error
>messages.

No I was thinking of an actual compiler crash if its get stuck trying to solve
some version of the halting problem and blows up. A compiler is just a
program after all and a very complex one to boot so hidden bugs are almost
a certainty.

A good one in the past was feeding this to some old versions of the Solaris
compiler:

class myabstractclass
{
:
virtual void func() = 1;
:
};

Caused a fairly comprehensive compiler internal error dump.

Öö Tiib

unread,
Apr 23, 2019, 12:47:22 PM4/23/19
to
It is. My experience of experimentations with complex compile-time
processing is that clang and gcc do crash ultra rarely. Might be very
good specialists there. When these run into some limits of
compile-time class generations, loops and recursions then these tell
that clearly and even suggest what switches to set. Linkers are
sometimes nasty in style of "collect2.exe: error: ld returned 5 exit status"
and that is it basically ... may be whatever but typically about some
ODR violation.

> A good one in the past was feeding this to some old versions of the Solaris
> compiler:
>
> class myabstractclass
> {
> :
> virtual void func() = 1;
> :
> };
>
> Caused a fairly comprehensive compiler internal error dump.

Most weird thing IMHO is IAR compiler: expensive,
anything above C 89 may results with something weird,
the IDE is more rotten than Code Blocks, lot of ways to
make every piece of it to crash, hang or even to blue-screen
whole Windows. People are careful like de-miners using it.
Like workbench from hell ... how can anyone trust to make
software with such a thing?

Tim Rentsch

unread,
Apr 28, 2019, 12:19:53 PM4/28/19
to
blt_4...@um7ygq6ykv2ca0gs.edu writes:

>> [example defining a constexpr variable using a constexpr
>> function]
>
> I don't really see the point of these syntatic games that
> constexpr allows. If its a constant expression then by
> definition you know the value when you write the code so why
> not just have "static const int one = 1"?

There are several reasons one might choose to define a constexpr
value symbolically rather than putting in the constant value
directly:

(1) The value may be calculable in principle but non-trivial
to calculate in practice.

(2) The value may depend in some way on the value of some
other constexpr value that we anticipate changing in
the future.

(3) The value may depend on some implementation-defined
value so it isn't really constant but varies from
implementation to implementation.

Tim Rentsch

unread,
Apr 28, 2019, 12:24:22 PM4/28/19
to
Keep in mind that the same result can be computed using C++11
constexpr. Changes in C++14 make constexpr functions more
convenient than in C++11, but in most cases what can be done
using constexpr functions in C++14 (or C++17) can also be done
in C++11.
0 new messages