Where in the C++14 Standard, does it prohibit the declaration of object a below?
class A{ int i = 1; public: A():i{1}{} };
int main()
{
constexpr A a{};
}
See live example
Note that I highlighted the word declaration, because I don't think bullet points (2.7.2) or (2.7.3), in §5.19[expr.const]p2 is an answer for the question.
[dcl.constexpr]p9:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.19). [...]
The error you're getting now is because your type is not a literal type. Your type is not a literal type because it does have a custom constructor, but doesn't have any constexpr constructor. The wording in the error message is rather clear about the exact requirements.
If you add a constexpr constructor (but not the default constructor), the error message changes:
class A{ int i = 1; public: A():i{1}{} constexpr A(int){} };
int main()
{
constexpr A a{};
}
Now the error message becomes
error: call to non-constexpr function ‘A::A()’
constexpr A a{};
This is the second part I bolded: it's not the initialiser that has to be a constant expression. You're right, your initialiser isn't an expression at all. It's the constructor call that must be a constant expression, and although it isn't expressed explicitly in the source code, it is an expression nonetheless. This is covered in [expr.const] rather clearly:
an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor (12.4) [...]
to which you already refer in your question.
Well, your default constructor is not constexpr. Therefore, you cannot create a default constructed constexpr object.
Related
Consider the code:
struct Foo
{
const char str[] = "test";
};
int main()
{
Foo foo;
}
It fails to compile with both g++ and clang++, spitting out essentially
error: array bound cannot be deduced from an in-class initializer
I understand that this is what the standard probably says, but is there any particular good reason why? Since we have a string literal it seems that the compiler should be able to deduce the size without any problem, similarly to the case when you simply declare an out-of-class const C-like null terminated string.
The reason is that you always have the possibility to override an in-class initializer list in the constructor. So I guess that in the end, it could be very confusing.
struct Foo
{
Foo() {} // str = "test\0";
// Implementing this is easier if I can clearly see how big `str` is,
Foo() : str({'a','b', 'c', 'd'}) {} // str = "abcd0"
const char str[] = "test";
};
Notice that replacing const char with static constexpr char works perfectly, and probably it is what you want anyway.
As mentioned in the comments and as answered by #sbabbi, the answer lies in the details
12.6.2 Initializing bases and members [class.base.init]
In a non-delegating constructor, if a given non-static data member or
base class is not designated by a mem-initializer-id (including the
case where there is no mem-initializer-list because the constructor
has no ctor-initializer) and the entity is not a virtual base class of
an abstract class (10.4), then
if the entity is a non-static data member that has a brace-or-equal-initializer , the entity is initialized as specified in
8.5;
otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed;
otherwise, the entity is default-initialized
12.6.2 Initializing bases and members [class.base.init]
If a given non-static data member has both a
brace-or-equal-initializer and a mem-initializer, the initialization
specified by the mem-initializer is performed, and the non-static data
member’s brace-or-equal-initializer is ignored. [ Example: Given
struct A {
int i = /∗ some integer expression with side effects ∗/ ;
A(int arg) : i(arg) { }
// ...
};
the A(int) constructor will simply initialize i to the value of arg,
and the side effects in i’s brace-or equal-initializer will not take
place. — end example ]
So, if there is a non-deleting constructor, the brace-or-equal-initializer is ignored, and the constructor in-member initialization prevails. Thus, for array members for which the size is omitted, the expression becomes ill-formed. §12.6.2, item 9, makes it more explicit where we it specified that the r-value initializer expression is omitted if mem-initialization is performed by the constructor.
Also, the google group dicussion Yet another inconsitent behavior in C++, further elaborates and makes it more lucid. It extends the idea in explaining that brace-or-equal-initializer is a glorified way of an in-member initialization for cases where the in-member initialization for the member does not exist. As an example
struct Foo {
int i[5] ={1,2,3,4,5};
int j;
Foo(): j(0) {};
}
is equivalent to
struct Foo {
int i[5];
int j;
Foo(): j(0), i{1,2,3,4,5} {};
}
but now we see that if the array size was omitted, the expression would be ill-formed.
But then saying that, the compiler could have supported the feature for cases when the member is not initialized by in-member constructor initialization but currently for the sake of uniformity, the standard like many other things, does not support this feature.
If the compiler was allowed to support what you described, and the size of str was deduced to 5,
Foo foo = {{"This is not a test"}};
will lead to undefined behavior.
Let's look at this sample of code:
class D
{
public:
constexpr D(int val) : i(val) { };
~D() { };
private:
int i;
};
D d(3);
According to the documentation, D should be constant initialized:
Only the following variables are constant initialized: [...]
2. Static or thread-local object of class type that is initialized by a
constructor call, if the constructor is constexpr and all constructor
arguments (including implicit conversions) are constant expressions,
and if the initializers in the constructor's initializer list and the
brace-or-equal initializers of the class members only contain constant
expressions.
Indeed, d is initialized by constructor call, the constructor of D is constexpr and my argument (3) is a constant expression.
However, to specify to the compiler the value of a variable can be evaluated at compile time, it is possible to use constexpr specifier. But, in this case, it won't compile because D is not a LiteralType because it define a non-trivial constructor.
So, in my snippet, is d really constant initialized? If so, why can't I use constexpr specifier?
So, in my snippet, is d really constant initialized? If so, why can't I use constexpr specifier?
Yes, it will be constant initialized. As you've quoted, constant initialization doesn't need the type to be a LiteralType. But constexpr does need it. Your type is not a LiteralType, so it cannot be a constexpr. But the type and constructor call fulfills the requirements of being constant initialization.
Btw., C++20 will have constinit. With this, you can make sure that a variable gets static initialized (which means constant initialization in your case).
You can check out constinit for your example on godbolt, as a further evidence that it compiles successfully, and you can see that the object is initialized at compile-time (not a requirement by the standard, but GCC does it).
This code compiles just fine on all big 4 compilers, even on -pedantic
struct S
{
constexpr S(int a) {}
};
constexpr int f(S a)
{
return 1;
}
int main()
{
int a = 0;
S s(a);
constexpr int b = f(s);
}
However, this shouldn't be so according to the standard... right? Firstly, s wouldn't be usable in constant expressions [expr.const]/3, because it fails to meet the criteria of being either constexpr, or, const and of enum or integral type.
Secondly, it is not constant-initialized [expr.const]/2 because the full expression of the initialization would not be a constant expression [expr.const]/10 due to a lvalue-to-rvalue conversion being performed on a variable (a) that is not usable in constant expressions when initializing the parameter of the constructor.
Are all these compilers just eliding the initialization of the parameter of the constructor because it has no side-effects, and is it standard conforming (I'm 99% sure it isn't, as the only way for it to so would be to make s constexpr, and to pass it either a const int or a int that is declared constexpr)?
I believe the magician's trick here is the copy c'tor of S. You omitted it, so a defaulted one is generated for you here. Now it's a constexpr function too.
[class.copy.ctor] (emphasis mine)
12 A copy/move constructor that is defaulted and not defined as
deleted is implicitly defined when it is odr-used ([basic.def.odr]),
when it is needed for constant evaluation ([expr.const]), or when it
is explicitly defaulted after its first declaration. [ Note: The
copy/move constructor is implicitly defined even if the implementation
elided its odr-use ([basic.def.odr], [class.temporary]). — end note ]
If the implicitly-defined constructor would satisfy the requirements
of a constexpr constructor ([dcl.constexpr]), the implicitly-defined
constructor is constexpr.
Does the evaluation of the copy c'tor run afoul of any of the points in [expr.const]/4? It does not. It doesn't perform an lvalue to rvalue conversion on any of the argument's members (there are none to perform the conversion on). It doesn't use its reference parameter in any way that will require said reference to be usable in a constant expression. So we indeed get a valid constant expression, albeit a non-intuitive one.
We can verify the above by just adding a member to S.
struct S
{
int a = 1;
constexpr S(int a) {}
};
Now the copy c'tor is trying to access an object that is not usable in a constant expression as part of its evaluation (via said reference). So indeed, compilers will complain.
I'm not sure if this is a compiler bug or if I misunderstand constexpr:
struct S{};
constexpr S s1{};
constexpr S s2;
struct test{
static constexpr auto t1 = s1;
static constexpr auto t2 = s2; //error here
};
GCC 4.8 is giving me an odd error "error: field initializer is not constant". Is s2 really not a constant? If so why?
For clarity I actually am using a bunch of empty structs in my code (for meta programming https://github.com/porkybrain/Kvasir) so I really am interested in this specific example.
Update: The code should compile, because [class.ctor]/5 reads:
The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (12.6.2) and an empty compound-statement. If that user-written default constructor would satisfy the requirements of a constexpr constructor (7.1.5), the implicitly-defined default constructor is constexpr.
And since S is just an empty struct, the implicitly defined default constructor is empty and thus satisfying constexpr requirements.
So here you are dealing with imperfection of the compilers, which you have to workaround somehow.
Old answer:
Clang emits more sensible error message:
main.cpp:3:13: error: default initialization of an object of const type 'const S'
requires a user-provided default constructor
constexpr S s2;
^
[dcl.constexpr]/9 provides the explanation and even almost exactly your code as an example:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have
literal type and shall be initialized.(...)
[ Example:
struct pixel {
int x, y;
};
constexpr pixel ur = { 1294, 1024 };// OK
constexpr pixel origin; // error: initializer missing
—end example ]
Consider the code:
struct Foo
{
const char str[] = "test";
};
int main()
{
Foo foo;
}
It fails to compile with both g++ and clang++, spitting out essentially
error: array bound cannot be deduced from an in-class initializer
I understand that this is what the standard probably says, but is there any particular good reason why? Since we have a string literal it seems that the compiler should be able to deduce the size without any problem, similarly to the case when you simply declare an out-of-class const C-like null terminated string.
The reason is that you always have the possibility to override an in-class initializer list in the constructor. So I guess that in the end, it could be very confusing.
struct Foo
{
Foo() {} // str = "test\0";
// Implementing this is easier if I can clearly see how big `str` is,
Foo() : str({'a','b', 'c', 'd'}) {} // str = "abcd0"
const char str[] = "test";
};
Notice that replacing const char with static constexpr char works perfectly, and probably it is what you want anyway.
As mentioned in the comments and as answered by #sbabbi, the answer lies in the details
12.6.2 Initializing bases and members [class.base.init]
In a non-delegating constructor, if a given non-static data member or
base class is not designated by a mem-initializer-id (including the
case where there is no mem-initializer-list because the constructor
has no ctor-initializer) and the entity is not a virtual base class of
an abstract class (10.4), then
if the entity is a non-static data member that has a brace-or-equal-initializer , the entity is initialized as specified in
8.5;
otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed;
otherwise, the entity is default-initialized
12.6.2 Initializing bases and members [class.base.init]
If a given non-static data member has both a
brace-or-equal-initializer and a mem-initializer, the initialization
specified by the mem-initializer is performed, and the non-static data
member’s brace-or-equal-initializer is ignored. [ Example: Given
struct A {
int i = /∗ some integer expression with side effects ∗/ ;
A(int arg) : i(arg) { }
// ...
};
the A(int) constructor will simply initialize i to the value of arg,
and the side effects in i’s brace-or equal-initializer will not take
place. — end example ]
So, if there is a non-deleting constructor, the brace-or-equal-initializer is ignored, and the constructor in-member initialization prevails. Thus, for array members for which the size is omitted, the expression becomes ill-formed. §12.6.2, item 9, makes it more explicit where we it specified that the r-value initializer expression is omitted if mem-initialization is performed by the constructor.
Also, the google group dicussion Yet another inconsitent behavior in C++, further elaborates and makes it more lucid. It extends the idea in explaining that brace-or-equal-initializer is a glorified way of an in-member initialization for cases where the in-member initialization for the member does not exist. As an example
struct Foo {
int i[5] ={1,2,3,4,5};
int j;
Foo(): j(0) {};
}
is equivalent to
struct Foo {
int i[5];
int j;
Foo(): j(0), i{1,2,3,4,5} {};
}
but now we see that if the array size was omitted, the expression would be ill-formed.
But then saying that, the compiler could have supported the feature for cases when the member is not initialized by in-member constructor initialization but currently for the sake of uniformity, the standard like many other things, does not support this feature.
If the compiler was allowed to support what you described, and the size of str was deduced to 5,
Foo foo = {{"This is not a test"}};
will lead to undefined behavior.