Does declaring a constexpr object marks the constructor as constexpr - c++

I just a have a problem in understanding when the compiler marks the constructor as constexpr.
If I write the following program:
struct S{ S() {}; }
constexpr S s{ };
Does this mean that the default constructor is marked as constexpr?

An implicitly-defined constructor is a constructor defined by the compiler implicitly when some contexts are encountered (see below). But, an explicitly-defined constructor is a constructor defined by the user, not by the compiler.
Now per [class.default.ctor]/4:
A default constructor that is defaulted and not defined as deleted is
implicitly defined when it is odr-used ([basic.def.odr]) to create an
object of its class type ([intro.object]), when it is needed for
constant evaluation ([expr.const]), or when it is explicitly defaulted
after its first declaration. 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 and an empty compound-statement. If
that user-written default constructor would be ill-formed, the program
is ill-formed. If that user-written default constructor would satisfy
the requirements of a constexpr constructor ([dcl.constexpr]), the
implicitly-defined default constructor is constexpr [..]
This paragraph just tells you that the non-deleted defaulted default constructor is implicitly-defined when it's odr-used or needed for constant evaluation or explicitly-defaulted after its first declaration.
Also, it tells you that that implicitly-defined constructor is the same as the user-written default constructor with an empty body and no member-initializer-list.
Then, it tells you that it's defined as constexpr if its corresponding user-written default constructor satisfies all of the [dcl.constexpr]/3 conditions.
That's, an implicitly or explicitly defaulted constructor will be implicitly-defined as constexpr if all requirements of [dcl.constexpr]/3 are met. On other hand, neither explicitly-defined nor explicitly-declared constructor is implicitly-defined as constexpr even if it satisfies all of [dcl.constexpr]/3 that's because you explicitly defined it. But if you explicitly mark it as constexpr, it will be a constexpr constructor, meanwhile, it shall satisfy all of [dcl.constexpr]/3 conditions.
So in your example:
struct S{ S() {}; }
constexpr S s{ };
That's ill-formed just because S is not a literal type and you're trying to call a non-constexpr constructor in a constant expression context which is not allowed per [const.expr]/(5.2)

A constructor is only (potentially) implicitly constexpr if either the whole constructor itself is implicitly declared or if it is defaulted with = default on its first declaration.
You are manually declaring the constructor and you are not defaulting it, so it will only be constexpr if you add the constexpr specifier to the declaration.
The shown constructor is therefore not constexpr and as a consequence constexpr S s{ }; will fail to compile because the initialization would call a non-constexpr constructor which isn't allowed in a constant expression. constexpr on a variable declaration does however require the initialization of the variable (including the evaluation of the initializer(s)) to be a constant expression.

The other answers give details about why the constructor in your snippet is not constexpr, and that's good to know.
However, I think, given how the question is phrased, that another answer is required.
Does declaring a constexpr object marks the constructor as constexpr
This means that you are thinking of the idea that the way you declare an object can affect the definition of its class.
That is not the case: once you write down the definition of a class, that's the definition, at it won't be altered by whatever objects of that class you declare/define.
In more complex scenario, when templates are involved, the code generated for a template class can depend on the objects you create; but the point is that you'd be talking of a templates class, whereas your question is about a class.

Related

Does "default" constructor mean "default parameters" or "provided by C++"?

I'm confused on what "default constructors" are because I'm getting two meanings from both my class and also online.
1) We have written a constructor ourselves, but we made the parameters set to default values.
2) We have NOT written ANY construct at all, but we can still "pretend" like we did and initialize an instance of a class using a C++ "provided constructor"
If the case is #2, what happens when you initialize? Garbage values?
Thanks!
[class.ctor]/4:
A default constructor for a class X is a constructor of class X
for which each parameter that is not a function parameter pack has a
default argument (including the case of a constructor with no
parameters). If there is no user-declared constructor for class X, a
non-explicit constructor having no parameters is implicitly declared
as defaulted ([dcl.fct.def]). An implicitly-declared default
constructor is an inline public member of its class.
If you do not provide any constructors, then C++ will synthesize a (possibly deleted) default constructor. The semantics of this synthesized constructor is specified in [class.ctor]/7:
A default constructor that is defaulted and not defined as deleted is
implicitly defined when it is odr-used to create an object of its class type ([intro.object]) or when it is explicitly defaulted after
its first declaration. 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 and an empty compound-statement. If that user-written default constructor would be ill-formed, the program is
ill-formed. If that user-written default constructor would satisfy the
requirements of a constexpr constructor, the implicitly-defined
default constructor is constexpr. Before the defaulted default
constructor for a class is implicitly defined, all the
non-user-provided default constructors for its base classes and its
non-static data members shall have been implicitly defined.
[ Note: An implicitly-declared default constructor has an
exception specification ([except.spec]). An explicitly-defaulted
definition might have an implicit exception specification, see
[dcl.fct.def]. — end note ]
Therefore, yes, the data members will be default initialized without an in-class member initializers. This leaves members of builtin types uninitialized.

C++ constexpr inheriting constructor

The following code compiles with GCC 8.2 but not with Clang 6.0.1:
// A struct named Foo.
struct Foo
{
// Data member of type 'int'.
int val;
// Default constructor (constexpr).
constexpr Foo() noexcept : val(0) {}
};
// A struct named Bar.
struct Bar : Foo
{
// Make use of the constructors declared in Foo.
using Foo::Foo;
// A constructor taking an object of type Foo.
// COMMENTING THIS CONSTRUCTOR SOLVE THE COMPILATION ISSUE.
constexpr Bar(Foo const obj) noexcept : Foo(obj) {}
};
// A struct named Test.
struct Test
{
// Data member of type 'Bar'.
Bar bar;
// A defaulted default constructor.
constexpr Test() noexcept = default;
};
// Main function.
int main() { return 0; }
Clang fails with the following message:
error: defaulted definition of default constructor is not constexpr
constexpr Test() noexcept = default;
I would like to understand why Clang is rejecting this code.
It looks like clang is relying on pre C++17 wording from C++14 section [class.inhctor]p3:
For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same signature in the complete class where the using-declaration appears or the constructor would be a default, copy, or move constructor for that class. Similarly, for each constructor template in the candidate set of inherited constructors, a constructor template is implicitly declared with the same constructor characteristics unless there is an equivalent user-declared constructor template ([temp.over.link]) in the complete class where the using-declaration appears. [ Note: Default arguments are not inherited. An exception-specification is implied as specified in [except.spec]. — end note ]
So in C++14:
using Foo::Foo;
means Bar does not inherit Foo's default constructor and Bar does not have a default constructor since it is inhibited by your declaration of:
constexpr Bar(Foo const obj) noexcept : Foo(obj) {}
Adding a default constructor to Bar fixes the problem see it live:
constexpr Bar() = default ;
The wording was changed in C++17 with the paper p0136r1: Rewording inheriting constructors (core issue 1941 et al) which was can see was accepted from Changes between C++14 and C++17 DIS
The following papers were moved at committee meetings, but their contents are too specific to call out as separate features: N3922, N4089, N4258, N4261, N4268, N4277, N4285, P0017R1, P0031R0, P0033R1, P0074R0, P0136R1, P0250R3, P0270R3, P0283R2, P0296R2, P0418R2, P0503R0, P0509R1, P0513R0, P0516R0, P0517R0, P0558R1, P0599R1, P0607R0, P0612R0
we can see p0136r1 removed [class.inhctor]:
Remove 12.9 class.inhctor, "Inheriting constructors".
I don't see any wording in p0136r1 that would restrict this case any more. The list of defect reports does not specifically cover this case but the wording changes seem consistent.
So it looks like gcc is correct here and we have a potential clang bug.
gcc 7 release notes
We also obtain a diagnostic in gcc pre 7.x (see it live). If we look at the gcc 7 release notes we see:
The default semantics of inherited constructors has changed in all modes, following P0136. Essentially, overload resolution happens as if calling the inherited constructor directly, and the compiler fills in construction of the other bases and members as needed. Most uses should not need any changes. The old behavior can be restored with -fno-new-inheriting-ctors, or -fabi-version less than 11.
Which seems to confirm the initial conclusion. If we use -fno-new-inheriting-ctors with a slightly modified version of your program it no longer compiles which backs up this was changed with P0136.
In C++14, default constructors can not be inherited.
§12.9 [class.inhctor] (emphasis mine)
3 For each non-template constructor in the candidate set of
inherited constructors other than a constructor having no parameters
or a copy/move constructor having a single parameter, a constructor is
implicitly declared with the same constructor characteristics unless
there is a user-declared constructor with the same signature in the
complete class where the using-declaration appears or the constructor
would be a default, copy, or move constructor for that class. ...
This basically means, that for your class Bar, the ctor will be implicitly defined - and means using Foo::Foo is not doing anything meaningful.
However, as you are having a separate constructor for Bar, this prevents the implicit definition of a default constructor.
The reason this works when you comment out your separate constexpr Bar(Foo const obj) ctor is because of
5 [ Note: Default and copy/move constructors may be implicitly
declared as specified in 12.1 and 12.8. —end note ]
§12.1/5 [class.ctor]
... If that user-written default constructor would satisfy the
requirements of a constexpr constructor (7.1.5), the
implicitly-defined default constructor is constexpr. ...
So, the implicitly declared constructor is declared as constexpr, which makes your code work and compile as expected.
You can remedy the issue by just explicitly defaulting the default ctor like this:
constexpr Bar() noexcept = default;
You can also take a look at Constexpr class: Inheritance?
The problem there is a bit different, but very similar to what you are seeing.
Sadly, I am unable to find the relevant parts in the C++17 standard. I assume the reasoning is the same, but can't find the reference to be 100% sure.

Why can't a destructor be marked constexpr?

In C++, you can declare many things as constexpr: variables, functions (including member functions and operators), constructors, and since C++1z, also if statements and lambda expressions. However, declaring a destructor constexpr results in an error:
struct X {
constexpr ~X() = default; // error: a destructor cannot be 'constexpr'
};
My questions:
Why can't a destructor be marked constexpr?
If I do not provide a destructor, is the implicitly generated destructor constexpr?
If I declare a defaulted destructor (~X() = default;), is it automatically constexpr?
As per the draft basic.types#10 possibly cv-qualified class type that has all of the following properties:
A possibly cv-qualified class type that has all of the following properties:
(10.5.1) - it has a trivial destructor,
(10.5.2) - it is either a closure type, an aggregate type, or has at
least one constexpr constructor or constructor template (possibly
inherited from a base class) that is not a copy or move constructor,
(10.5.3) - if it is a union, at least one of its non-static data
members is of non-volatile literal type
(10.5.4) - if it is not
a union, all of its non-static data members and base classes are of
non-volatile literal types.
Ques 1: Why a destructor cannot be marked as constexpr?
Because only trivial destructors are qualified for constexpr
Following is the relevant section of the draft
A destructor is trivial if it is not user-provided and if:
(5.4) — the destructor is not virtual,
(5.5) — all of the direct base classes of its class have trivial
destructors, and
(5.6) — for all of the non-static data members of its class that are
of class type (or array thereof), each such class has a trivial
destructor.
Otherwise, the destructor is non-trivial.
Ques 2: If I do not provide a destructor, is the implicitly generated destructor constexpr?
Yes, because implicitly generated destructor is trivial type, so it is qualified for constexpr
Ques 3: If I declare a defaulted destructor (~X() = default;), is it automatically constexpr?
Indeed, this destructor is user-declared and implicitly-generated and thus it is qualified for constexpr.
I'm not able to find any direct reference that only trivial destructors are qualified for constexpr but if the destructor is not trivial then it is for sure that class type is not cv-qualified. So it kind of implicit as you can't define a destructor for cv-qualified class.
C++20 Update
Since C++20, user defined destructors can also be constexpr under certain conditions.
dcl.constexpr/3:
The definition of a constexpr function shall satisfy the following
requirements:
its return type (if any) shall be a literal type;
each of its parameter types shall be a literal type;
it shall not be a coroutine ([dcl.fct.def.coroutine]);
if the function is a constructor or destructor, its class shall not have any
virtual base classes;
its function-body shall not enclose ([stmt.pre])
a goto statement,
an identifier label ([stmt.label]),
a definition of a variable of non-literal type or of static or thread
storage duration.
If what you're looking for is reasoning behind the restriction, have a look at this paper which clearly states that the restriction is artificial - there is no intrinsic property of destructors that prevent them from working in constexpr contexts, and indeed compiler implementors agree that supporting them in constexpr contexts will be trivial to implement.
I guess the C++ standards committee originally placed the restriction in C++11 because they didn't want to deal with destructors at that time and it was easier to just rule them out entirely.
Since C++20, a constructor may be marked constexpr; I don’t know if it says anywhere specifically “a destructor may be constexpr”, but the draft standard includes the following text in section 9.2.5 paragraph 5:
The definition of a constexpr destructor whose function-body is not = delete shall additionally satisfy the
following requirement:
for every subobject of class type or (possibly multi-dimensional) array thereof, that class type shall
have a constexpr destructor.
This also now has a useful function because C++20 also allows new and delete in constexpr contexts, allowing things like vector and string to work at compile time without hacks (although I believe C++20 does not actually include changes to the standard library to allow for this, it is possible to implement something with the same API and behaviour as vector that works completely at compile time).
Why a destructor cannot be marked as constexpr?
The C++11 standard is specific about use of constexpr for consructors and non-static member function. It does not say anything specific about destructor. One may assume that destructors are to be treated as non-static member functions.
constexpr can be used only for const member functions. Since a destructor cannot be const member function, it cannot be qualified as a constexpr member function.
If I do not provide a destructor, is the implicitly generated destructor constexpr.
Since use of
constexpr ~X() = default;
is an error, it makes sense to me that the compiler generated destructor is not a constexpr function. I can't find anything in the standard to justify my statement. I am guessing.
If I declare a defaulted destructor (~X() = default;), is it automatically constexpr
I think not. Once again, I can't find anything in the standard to justify my statement. I am guessing.
FWIW, g++ compiles and builds the following program just fine.
struct X {
constexpr X(int i) : i_(i) {}
~X() = default;
int i_;
};
int main()
{
const X x(10);
}
Reference say's:
constexpr destructors
In most cases, in order to create an object of a type T in a constant
expression, the destruction of T must be trivial. However, non-trivial
destructors are an important component of modern C++, partly due to
widespread usage of the RAII idiom, which is also applicable in
constexpr evaluations. Non-trivial destructors could be supported in
constant expressions, as follows:
Allow destructors to be marked as constexpr
Make defaulted destructors constexpr if they only invoke constexpr destructors
For constexpr variables, require that evaluating the destructor is a constant expression (except that the object being destroyed may be
modified in its own destructor
However, no compelling use cases are known for such a feature, and
there would be a non-trivial implementation cost ensuring that
destructors are run at the right times.
A destructor can't be constexpr because constexpr functions can't have side effects and destructors by definition are only useful through side effects. In short, it would be useless to have a destructor that is constexpr.
A object cannot be constexpr if its destructor is non-trivial. A defaulted one, if trivial, will be considered constexpr
Live
From [class.dtor]
Each decl-specifier of the decl-specifier-seq of a destructor declaration (if any) shall be friend, inline, or virtual.
Missing from it, constexpr. So you could just take it as: because the standard says soTM

Constexpr class: Inheritance?

First of all, I'm working with Clang 3.4.1
I'm writting a global variable which has to serve as a placeholder on compile-time contexts (Primarily as value template parameter). For that purpose, I have written a constexpr class named chameleon (It mimics the behaviour of any runtime value):
struct chameleon
{
template<typename T>
constexpr operator T() const
{
return T{};
}
constexpr chameleon() = default;
};
Since both the conversion operator and the constructor are specified as constexpr, I'm able to make instances of that class at compile-time. For example:
template<int foo>
struct bar
{};
using mytype = bar<chameleon{}>;
Since this works, and I use it in other places, I decided to write such placeholder type
just inheriting from chameleon:
template<std::size_t I>
struct placeholder : public chameleon
{
using chameleon::chameleon;
};
I'm using C++11, so I just used the "new" (C++11 has three years...) inheriting constructors feature.
When declaring the placeholder variable:
constexpr const placeholder<0> _1;
The compiler rejects the code saying it expects an user-defined default ctor for initialization. So "Well, inheriting ctors doesn't propagate constexpr, or something like that" is what I though. Then I changed the using to a default ctor declaration:
template<std::size_t I>
struct placeholder : public chameleon
{
constexpr placeholder() = default;
};
Now the compiler says:
error: default initialization of an object of const type 'const placeholder<0>' requires a user-provided default constructor
If I change the = default to a manually defined empty constructor (constexpr placeholder() {}) then it works, but the constructor is not evaluated as constexpr and the usage of the
_ placeholder in compile-time contexts is invalid (The common is not a constant expression error). The same for manually calling the base ctor.
My question is: Whats wrong with inheritance and constexprconstructors? Is there any way to use inheritance when writting constexpr classes?
EDIT: I have a division-by-zero bug, the code using the manually written ctor works perfectly. On the other hand, I don't understand why neither the inheriting constructor or the default constructor declaration worked. The question remains there.
So "Well, inheriting ctors doesn't propagate constexpr, or something like that" is what I thought
That's not the issue; default and copy/move constructors cannot be inherited. If you don't explicitly define or default them, they'll be implicitly defined following the usual rules.
§12.9 [class.inhctor]
3 For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same
signature in the complete class where the using-declaration appears or the constructor would be a default, copy, or move constructor for that class. ...
5 [ Note: Default and copy/move constructors may be implicitly declared as specified in 12.1 and 12.8. —end note ]
Consequently, placeholder, with or without the using declaration for inheriting chameleon's constructors, will have a default constructor implicitly defined, and this constructor will be constexpr.
§12.1/5 [class.ctor]
... If that user-written default constructor would satisfy the requirements of a constexpr constructor (7.1.5), the implicitly-defined default constructor is constexpr. ...
Your class, with a user provided default constructor, satisfies the requirements in §7.1.5/4 for a constexpr constructor. The error you saw was because of the next part.
As for why you must provide a constructor definition for a const object, let's take a look at your class.
struct chameleon
{
template<typename T>
constexpr operator T() const
{
return T{};
}
constexpr chameleon() = default;
};
This class is both trivial (9/6) and standard layout (9/7), hence it is a POD (9/10). A POD is uninitialized by default, so a const POD without an initializer would be uninitialized, and immutable, making it pretty much worthless (or I'm not able to think of any use cases, at least).
By providing a default constructor, the class is no longer a POD, and will be default initialized.
As #Casey points out in the comments, this requirement is listed in §8.5/7
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.

In C++0x, do non-static data member initializers override the implicit copy constructor?

According to N2628 related to c++0x, non-static data member initializers can be overridden by explicitly defined constructors, but it appears to be slightly nebulous about the implicitly defined copy constructor.
In particular, I've noticed that with Apple clang version 3.0, the behavior varies depending on whether the struct (or class) is a POD.
The following program returns output "1", which indicates that the copy-constructor is ignoring the right-hand-side, and instead substituting the new non-static data member initializer (in this example, the boolean true value for X::a).
#include <iostream>
#include <string>
struct X
{
std::string string1;
bool a = true;
};
int main(int argc, char *argv[])
{
X x;
x.a = false;
X y(x);
std::cout << y.a << std::endl;
}
However, confusingly, if you comment out string1:
// std::string string1;
then the behavior works as I expected (the output is "0"), presumably because there is no implicitly generated copy-constructor, and therefore the data is copied.
Does the C++0x specification really suggest that it is a good idea to allow the implicitly defined copy-constructor to not copy the contents of the right-hand side? Isn't that less useful and unintuitive? I find the non-static member initializer functionality to be quite convenient, but if this is the correct behavior, then I will explicitly avoid the feature due to its tricky and unintuitive behavior.
Please tell me I'm wrong?
UPDATE: This bug has been fixed in the Clang source repository. See this revision.
UPDATE: This bug appears fixed in Apple clang version 3.1 (tags/Apple/clang-318.0.45) (based on LLVM 3.1svn). This version of clang was distributed as part of Xcode 4.3 for Lion.
It isn't shadowy after all, see highlighted parts of the standards excerpt:
The section on defaulted copy/move constructors (§ 12.8) is a bit too lengthy to quote in it's entirety. The low-down is that non-static member fields with initializers are still simply copied by the defaulted copy/move constructor
§ 12.8:
-6. The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. See
also the example in 12.6.2. —end note ] The order of initialization is the same as the order of initialization
of bases and members in a user-defined constructor (see 12.6.2). Let x be either the parameter of the
constructor or, for the move constructor, an xvalue referring to the parameter. Each base or non-static data
member is copied/moved in the manner appropriate to its type:
if the member is an array, each element is direct-initialized with the corresponding subobject of x;
if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
otherwise, the base or member is direct-initialized with the corresponding base or member of x.
Virtual base class subobjects shall be initialized only once by the implicitly-defined copy/move constructor
This is the sample referred to:
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-equalinitializer will not take place. —end example ]
For completeness, the corresponding section on the defaulted default constructor:
§ 12.1
-6. A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (3.2) to create an object of its class type (1.8) or when it is explicitly defaulted after its first declaration.
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 be ill-formed, the program is ill-formed.
If that user-written default constructor would satisfy the requirements of a constexpr constructor (7.1.5),
the implicitly-defined default constructor is constexpr. Before the defaulted default constructor for a
class is implicitly defined, all the non-user-provided default constructors for its base classes and its nonstatic
data members shall have been implicitly defined. [ Note: An implicitly-declared default constructor
has an exception-specification (15.4). An explicitly-defaulted definition might have an implicit exception-specification,
see 8.4. —end note ]