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

static constexpr with inner type doesn't compile

71 views
Skip to first unread message

Juha Nieminen

unread,
May 23, 2018, 2:24:51 AM5/23/18
to
Why doesn't the last line compile?

//------------------------------------------------------------
struct Test1 {};
struct Test2 { constexpr Test2() {} };

struct Container
{
struct Test3 {};
struct Test4 { constexpr Test4() {} };

static constexpr Test1 object1; // Compiles
static constexpr Test2 object2; // Compiles
static constexpr Test3 object3; // Compiles
static constexpr Test4 object4; // Does not compile
};
//------------------------------------------------------------

Ian Collins

unread,
May 23, 2018, 3:27:16 AM5/23/18
to
Compilers are trying to tell us something:

$ clang++ -std=c++17 /tmp/tt.cc
/tmp/tt.cc:13:28: error: constexpr variable 'object4' must be
initialized by a constant expression
static constexpr Test4 object4; // Does not compile
^~~~~~~
/tmp/tt.cc:13:28: note: undefined constructor 'Test4' cannot be used in
a constant expression
/tmp/tt.cc:8:30: note: declared here
struct Test4 { constexpr Test4() {} };
^
1 error generated.
$ g++ -std=c++17 /tmp/tt.cc
/tmp/tt.cc:10:28: error: constexpr static data member ‘object1’ must
have an initializer
static constexpr Test1 object1; // Compiles
^~~~~~~
/tmp/tt.cc:11:28: error: constexpr static data member ‘object2’ must
have an initializer
static constexpr Test2 object2; // Compiles
^~~~~~~
/tmp/tt.cc:12:28: error: constexpr static data member ‘object3’ must
have an initializer
static constexpr Test3 object3; // Compiles
^~~~~~~
/tmp/tt.cc:13:28: error: constexpr static data member ‘object4’ must
have an initializer
static constexpr Test4 object4; // Does not compile
^~~~~~~

Change the objects to object[1234]():

$ clang++ -std=c++17 -c /tmp/tt.cc
$ g++ -std=c++17 -c /tmp/tt.cc

--
Ian.

Paavo Helde

unread,
May 23, 2018, 5:07:11 AM5/23/18
to
This will just declare a bunch of functions, which is probably not what
OP had in mind. One ought to use object[1234]{}, but this doesn't
compile for Test4 either:

>g++ main.cpp
main.cpp:15:33: error: 'constexpr Container::Test4::Test4()' called in a
constant expression before its definition is complete
static constexpr Test4 object4{}; // Does not compile
^

Ian Collins

unread,
May 23, 2018, 6:08:42 AM5/23/18
to
On 23/05/18 21:07, Paavo Helde wrote:
> On 23.05.2018 10:27, Ian Collins wrote:
>> On 23/05/18 18:24, Juha Nieminen wrote:
>>> Why doesn't the last line compile?
>>>
>>> //------------------------------------------------------------
>>> struct Test1 {};
>>> struct Test2 { constexpr Test2() {} };
>>>
>>> struct Container
>>> {
>>> struct Test3 {};
>>> struct Test4 { constexpr Test4() {} };
>>>
>>> static constexpr Test1 object1; // Compiles
>>> static constexpr Test2 object2; // Compiles
>>> static constexpr Test3 object3; // Compiles
>>> static constexpr Test4 object4; // Does not compile
>>> };
>>> //------------------------------------------------------------

<snip>

>> Change the objects to object[1234]():
>
> This will just declare a bunch of functions, which is probably not what
> OP had in mind. One ought to use object[1234]{}, but this doesn't
> compile for Test4 either:

That was what I meant to type...

> >g++ main.cpp
> main.cpp:15:33: error: 'constexpr Container::Test4::Test4()' called in a
> constant expression before its definition is complete
> static constexpr Test4 object4{}; // Does not compile
> ^

I wonder if it is to do with where inline member function definitions
are parsed? The nested class is within the scope of Container, so where
is a local class constructor complete?

--
Ian.

Alf P. Steinbach

unread,
May 23, 2018, 6:55:02 AM5/23/18
to
It's clearly a bug, but the question is whether it's in the compilers or
in the standard.

I'd guess that the compilers somehow incorrectly regard `Test4` as
incomplete because the containing class `Container` is incomplete.

As a workaround it "should" work to just provide the explicit
initialization that the error messages quoted else-thread, refer to.


Cheers!,

- Alf

Sam

unread,
May 23, 2018, 8:30:41 AM5/23/18
to
Juha Nieminen writes:

> Why doesn't the last line compile?
>
> //------------------------------------------------------------
> struct Test1 {};
> struct Test2 { constexpr Test2() {} };
>
> struct Container
> {
> struct Test3 {};
> struct Test4 { constexpr Test4() {} };
>
> static constexpr Test1 object1; // Compiles
> static constexpr Test2 object2; // Compiles
> static constexpr Test3 object3; // Compiles
> static constexpr Test4 object4; // Does not compile
> };

The definition of the "Container" class is not complete until its closing
brace.

A constexpr object must refer to a complete class, amongst all the other
requirements. "Test4" refers to a member of an incomplete class, at the time
it's used.

You just can't have a `constexpr` class member instance of the class itself,
or of any inner classes.

Ben Bacarisse

unread,
May 23, 2018, 9:54:19 AM5/23/18
to
I'm piggy-backing as much as replying...

Sam <s...@email-scan.com> writes:

> Juha Nieminen writes:
>
>> Why doesn't the last line compile?
>>
>> //------------------------------------------------------------
>> struct Test1 {};
>> struct Test2 { constexpr Test2() {} };
>>
>> struct Container
>> {
>> struct Test3 {};
>> struct Test4 { constexpr Test4() {} };
>>
>> static constexpr Test1 object1; // Compiles
>> static constexpr Test2 object2; // Compiles
>> static constexpr Test3 object3; // Compiles
>> static constexpr Test4 object4; // Does not compile
>> };

A data point... gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0 complains about all
four lines with "constexpr static data member ‘object1’ must have an
initializer". Adding = {} to each makes it complain only about the last
one with ‘constexpr Container::Test4::Test4()’ called in a constant
expression before its definition is complete".

> The definition of the "Container" class is not complete until its closing brace.
>
> A constexpr object must refer to a complete class, amongst all the
> other requirements. "Test4" refers to a member of an incomplete class,
> at the time it's used.

That's the bit that confuses me. Container is not complete, but Test4
looks complete. Adding

struct Test5 { int i; };
...
static constexpr Test5 object5 = {};

provokes no complaint (for gcc or clang) and Test5 looks just as
complete or incomplete as Test4. The issue must be specific to classes
with constructors and I confess to not have read what C++ says about a
class being complete.

> You just can't have a `constexpr` class member instance of the class
> itself, or of any inner classes.

gcc and clang both permit such a thing in the case of Test5, so is that
a compiler bug?

--
Ben.

Juha Nieminen

unread,
May 24, 2018, 5:16:45 AM5/24/18
to
Ben Bacarisse <ben.u...@bsb.me.uk> wrote:
> A data point... gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0 complains about all
> four lines with "constexpr static data member ???object1??? must have an
> initializer". Adding = {} to each makes it complain only about the last
> one with ???constexpr Container::Test4::Test4()??? called in a constant
> expression before its definition is complete".

I wonder if gcc requiring the initializer (which clang doesn't) is
also required by the standard, or whether it's just a interpretation
by the developers of gcc.

One would think that at the very least the struct having an
explicit default constructor (ie. Test2) wouldn't require an
explicit initialization, but apparently gcc thinks otherwise.

As you note, both gcc and clang scoff at Test4 in particular,
which is the only one that's inside the outer struct and has
an explicit (albeit constexpr) constructor. Both compilers seem
to be interpreting the strandard in the same way, so maybe it is
so in the standard as well? But why? What exactly is the rule?

Alf P. Steinbach

unread,
May 24, 2018, 5:54:21 AM5/24/18
to
If it is specified by the standard it's probably about "user declared"
constructor or not. Maybe tied in with notion of aggregates.

Cheers!,

- Alf


Tim Rentsch

unread,
May 24, 2018, 6:23:19 PM5/24/18
to
I don't know the answer to your question, but the following
may shed some light on the subject.

The code below

struct Foo {
struct Bas {
constexpr Bas(){}
};

// static constexpr Bas t1; // eh? // 1
// static constexpr Bas t2 = Bas(); // eh?
// static constexpr Bas t3 = Bas{}; // eh?
// static constexpr Bas t4 = {}; // eh?
static constexpr Bas t5();
// static constexpr Bas t6{}; // eh?
};

compiles without problems as C++11, C++14, and C++17, under both
clang (5.0.0-3~16.04.1) and g++ (Ubuntu 5.4.0-6ubuntu1~16.04.9).

If any of the lines marked "eh?" are uncommented, the code
fails to compile in all versions.

If the definition of struct Bas is moved outside of struct Foo,
the code compiles in all cases, including uncommented all "eh?"
lines, except that the first "eh?" line is accepted only with
clang as C++17.

Finding it difficult to give definitive answers to such seemingly
simple questions is part of why I think C++ is too complicated.
I'm surprised that the resident experts apparently aren't sure
either.

Juha Nieminen

unread,
May 25, 2018, 1:22:10 AM5/25/18
to
Tim Rentsch <t...@alumni.caltech.edu> wrote:
> struct Foo {
> struct Bas {
> constexpr Bas(){}
> };
>
> static constexpr Bas t5();
> };

That's not a static member. It's a static function declaration.

Tim Rentsch

unread,
Jun 13, 2018, 8:53:26 PM6/13/18
to
Oh yes. I had forgotten this delightful ambiguity.
0 new messages