Given the following code:
struct f {
};
int main(){
constexpr f f1 ;
//const f f1 ; // This also has the same issue
//constexpr f f1 = {} ; //This works
}
clang and gcc disagree over whether it is valid, with clang providing the following diagnostic (see it live):
error: default initialization of an object of const type 'const f' without a user-provided default constructor
constexpr f f1 ;
^
{}
As far as I can tell f
is a literal type and it is initialized by the implicitly defaulted constructor which should allow it to be declared constexpr. Who is correct here?
Note, clang does accept the declaration of f1
if I explicitly add a constexpr default constructor:
constexpr f() {} ;
Does the answer change is f
is not an aggregate?
If that user-defined default constructor would satisfy the requirements of a constexpr constructor, the implicitly defined default constructor is a constexpr constructor. A constexpr constructor is implicitly inline.
A constructor that is declared with a constexpr specifier is a constexpr constructor. Previously, only expressions of built-in types could be valid constant expressions. With constexpr constructors, objects of user-defined types can be included in valid constant expressions.
constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.
A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later. So, what does constexpr mean?
If we start from the draft C++14 standard section 7.1.5
[dcl.constexpr] we can find the requirements for a constexpr object declaration are:
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).
So is f
is a literal type?
Section 3.9
[basic.types] says:
A type is a literal type if it is:
and covers classes in the following bullet:
a class type (Clause 9) that has all of the following properties
- it has a trivial destructor,
- it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and
- all of its non-static data members and base classes are of non-volatile literal types.
So we are okay on the first and third bullet. To cover the second bullet, we could note that f
is an aggregate but if we modify the example slightly, for example if f
looked like this:
struct f {
private:
int x = 0 ;
} ;
which would not be an aggregate in either C++11 or C++14 but the issue would still exist. Then we need to show it has a constexpr constructor. Does f
have a constexpr constructor?
As far as I can tell section 12.1
[class.ctor] says yes:
[...] If that user-written default constructor would satisfy the requirements of a constexpr constructor (7.1.5), the implicitly-defined default constructor is constexpr. [...]
But we are unfortunately required to have a user-provided constructor by section 8.5
which says:
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.
So it looks like clang is correct here and if we look at the following clang bug report: "error: default initialization of an object of const type 'const Z' requires a user-provided default constructor" even when no constructor needed. So clang is basing their lack of support for this due to defect report 253 which does not currently have a proposed wording and it says (emphasis mine):
Paragraph 9 of 8.5 [dcl.init] says:
If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for an object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.
What if a const POD object has no non-static data members? This wording requires an empty initializer for such cases
[...]
Similar comments apply to a non-POD const object, all of whose non-static data members and base class subobjects have default constructors. Why should the class of such an object be required to have a user-declared default constructor?
The defect report is still open but the last comment on it says:
If the implicit default constructor initializes all subobjects, no initializer should be required.
and also notes:
This issue should be brought up again in light of constexpr constructors and non-static data member initializers.
Note the constraints on const qualified types moved around in section 8.5
since the defect report came about. This was due to proposal N2762: Not so Trivial Issues with Trivial which was pre C++11.
Although the defect report is still open, given the constexpr changes and non-static data member initializers it does not seem like a necessary restriction anymore. Especially considering the following requirement on a constexpr constructor:
- every non-variant non-static data member and base class sub-object shall be initialized (12.6.2);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With