C++ constexpr inheriting constructor - c++

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.

Related

=default in declaration vs definition

I know that instead of writing:
class A {
public:
A(A&&) noexcept = default;
};
One should better write
class A {
public:
A(A&&) noexcept;
};
inline A::A(A&&) noexcept = default;
The reasons I've heard are:
It avoids the constructor becomes deleted. Compiler will give an error if it is unable to define the function.
The move constructor is declared noexcept even if some of the member fields' move constructor are not annotated with noexcept.
Could someone explain a bit more about the theory behind the differences?
Only declaration is used to describe the class/method, so when doing
class A {
public:
A(A&&) noexcept;
};
You might even implement A::A(A&&) as you want (definition can be in different TU)
When you implement it with:
A::A(A&&) noexcept = default;
Compiler has to generate the method (it cannot tell if it is implicitly deleted as declaration precise method exists), and provides diagnostic if it can't.
But when you declare it inside the class:
class A {
public:
A(A&&) noexcept = default;
};
It is "part" of declaration. so it might be implicitly deleted (because of member or base class).
Same apply for noexcept.
An other advantage to put definition in dedicated TU, it that definition of required dependencies can be only in that TU, instead of each place where the method would be generated. (Useful for pimpl idiom for example).
One disadvantage of split definition and declaration is that the method is now "user provided", that may affect traits as trivially_constructible/copyable/...
The behavior is covered in [dcl.fct.def.default]p3 which says:
If a function that is explicitly defaulted is declared with a noexcept-specifier that does not produce the same
exception specification as the implicit declaration (18.4), then
(3.1) — if the function is explicitly defaulted on its first declaration, it is defined as deleted;
(3.2) — otherwise, the program is ill-formed.
Note the wording changes in C++20 but the intent is the same for this case. I find the C++17 wording simpler to grok.
For example given:
struct S {
S( S&& ) noexcept(false) = default;
};
The move constructor is defined as deleted since due to [except.spec]p7:
An implicitly-declared constructor for a class X, or a constructor without a noexcept-specifier that is defaulted
on its first declaration, has a potentially-throwing exception specification if and only if any of the following
constructs is potentially-throwing:
(7.1) — a constructor selected by overload resolution in the implicit definition of the constructor for class X to
initialize a potentially constructed subobject, or
(7.2) — a subexpression of such an initialization, such as a default argument expression, or,
(7.3) — for a default constructor, a default member initializer.
none of the cases hold.
If we got back to [dcl.fct.def.default]p3 it says otherwise the program is ill-formed. Ill-formed programs require a diagnostic so if we modify the first example as follows (see it live):
struct S {
S( S&& ) noexcept(false) ;
private:
int i;
};
S::S( S&&) noexcept(false) = default ;
it will produce a diagnostic e.g.:
error: function 'S::S(S&&)' defaulted on its redeclaration with an exception-specification that differs from the implicit exception-specification 'noexcept'
S::S( S&&) noexcept(false) = default ;
^
Note clang bug related to this case, it seems they are not following Defect Report 1778.
You may want to note Declaring a function as defaulted after its first declaration which covers some the optimization/interface issues.

Is the compiler generated constructor constexpr by default?

The following code compiles successfully with both clang++ 3.8.0 and g++ 7.2.0 (compilation flags are -std=c++14 -Wall -Wextra -Werror -pedantic-errors):
struct Foo
{
constexpr operator bool() const
{
return false;
}
};
int main()
{
constexpr bool b = Foo{};
(void)b;
}
Is such behavior of the compilers standard compliant? Note that adding any member (like int i;) to the Foo class doesn't change anything.
Yes, the implicit constructor is constexpr this case. In general, it depends on the sub-objects.
[class.ctor]
A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (6.2)
to create an object of its class type (4.5) 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 (15.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 (10.1.5), the
implicitly-defined default constructor is constexpr. ... [snip]
Relevant requirements of a constexpr constructor:
[dcl.constexpr]
the class shall not have any virtual base classes;
for a non-delegating constructor, every constructor selected to initialize non-static data members and
base class subobjects shall be a constexpr constructor;
Yes it is. The default constructor that a compiler generates, and the trivial constructor
Foo() = default;
both enable you to write constexpr bool b = Foo{};, assuming all class members can be constructed constexpr. Note that if you had written
Foo(){}
then constexpr would not be allowed. (An important difference between default and a constructor provided with an empty body.)

Does it violate the standard for a non-default-constuctible struct to lack a user-defined constructor?

It is possible to define a struct (a) that has no user-defined constructors, and (b) for which a default constructor cannot be generated. For example, Foo in the below:
struct Baz
{
Baz(int) {}
};
struct Foo
{
int bar;
Baz baz;
};
You can still create instances of Foo using aggregate initialization:
Foo foo = { 0, Baz(0) };
My normal compiler (VS2012) will grudgingly accept this, but it raises 2 warnings:
warning C4510: 'Foo': default constructor could not be generated.
warning C4610: struct 'Foo' can never be instantiated - user defined constructor required
Of course, I've just proved warning #2 wrong--you can still instantiate it using aggregate initialization. The online compilers I've tried are happy enough to accept the above, so I'm guessing VS2012 is just being overly-aggressive with this warning. But I'd like to be sure--is this code ok, or does it technically violate some obscure part of the standard?
The standard explicitly allows cases like Foo in [12.1p4]:
[...] If there is no user-declared constructor for
class X, a constructor having no parameters is implicitly declared as defaulted [...] A defaulted default constructor for class X is defined as
deleted if:
[...]
any potentially constructed subobject, except for a non-static data member with a brace-or-equal-initializer, has class type M (or array
thereof) and either M has no default constructor or overload
resolution (13.3) as applied to M’s default constructor results in an
ambiguity or in a function that is deleted or inaccessible from the
defaulted default constructor
[...]
Baz has no default constructor, so the emphasised part above applies (emphasis mine).
There's nothing 'undefined' or 'ill-formed' about such cases. The implicitly declared default constructor is defined as deleted, that's all. You could do the same thing explicitly, and it would still be just as valid.
The definition for aggregates is in [8.5.1p1]. For C++14, it is:
An aggregate is an array or a class (Clause 9) with no user-provided
constructors (12.1), no private or protected non-static data members
(Clause 11), no base classes (Clause 10), and no virtual functions
(10.3).
The 'no user-provided' part allows you to use = delete on all constructors that could possibly be implicitly declared (making them user-declared, but not user-provided) and the class would still be an aggregate, allowing you to use aggregate initialization on it.
As for warning C4610, I've encountered it before myself and reported it. As you can see, it's been fixed in the upcoming version of VC++.
It may be worth mentioning that the example I used in the bug report is taken directly from the standard, where it's treated as well-formed ([12.2p5.4]:
struct S { int mi; const std::pair<int,int>& mp; };
S a { 1, {2,3} };
This is similar to your case, but here, the implicitly declared default constructor is defined as deleted because the class has a non-static member of reference type that has no initializer.
Granted, it's only an example, but I think it's an additional indication that there's nothing wrong with these cases.
That's not actually aggregate initialization, it's uniform, which is only recently supported by VS. This warning is simply them not correctly updating it to reflect that that type can now be uniform initialized.
Aggregates may not have user-defined non-defaulted non-deleted constructors, and the rules for aggregate UDTs are that each member must also be an aggregate. Therefore, Baz is not an aggregate and as a direct result, neither can Foo be.

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.

Purpose of Explicit Default Constructors

I recently noticed a class in C++0x that calls for an explicit default constructor. However, I'm failing to come up with a scenario in which a default constructor can be called implicitly. It seems like a rather pointless specifier. I thought maybe it would disallow Class c; in favor of Class c = Class(); but that does not appear to be the case.
Some relevant quotes from the C++0x FCD, since it is easier for me to navigate [similar text exists in C++03, if not in the same places]
12.3.1.3 [class.conv.ctor]
A default constructor may be an explicit constructor; such a constructor will be used to perform default-initialization or value initialization (8.5).
It goes on to provide an example of an explicit default constructor, but it simply mimics the example I provided above.
8.5.6 [decl.init]
To default-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
8.5.7 [decl.init]
To value-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
In both cases, the standard calls for the default constructor to be called. But that is what would happen if the default constructor were non-explicit. For completeness sake:
8.5.11 [decl.init]
If no initializer is specified for an object, the object is default-initialized;
From what I can tell, this just leaves conversion from no data. Which doesn't make sense. The best I can come up with would be the following:
void function(Class c);
int main() {
function(); //implicitly convert from no parameter to a single parameter
}
But obviously that isn't the way C++ handles default arguments. What else is there that would make explicit Class(); behave differently from Class();?
The specific example that generated this question was std::function [20.8.14.2 func.wrap.func]. It requires several converting constructors, none of which are marked explicit, but the default constructor is.
This declares an explicit default constructor:
struct A {
explicit A(int a1 = 0);
};
A a = 0; /* not allowed */
A b; /* allowed */
A c(0); /* allowed */
In case there is no parameter, like in the following example, the explicit is redundant.
struct A {
/* explicit is redundant. */
explicit A();
};
In some C++0x draft (I believe it was n3035), it made a difference in the following way:
A a = {}; /* error! */
A b{}; /* alright */
void function(A a);
void f() { function({}); /* error! */ }
But in the FCD, they changed this (though, I suspect that they didn't have this particular reason in mind) in that all three cases value-initialize the respective object. Value-initialization doesn't do the overload-resolution dance and thus won't fail on explicit constructors.
Unless explicitly stated otherwise, all standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.
(This answer focus specifically on explicit default constructors which have no parameters)
Case #1 [C++11 through C++20]: Empty {} copy-list-initialization for non-aggregates prohibits use of explicit default constructors
As governed by [over.match.list]/1 [emphasis mine]:
When objects of non-aggregate class type T are list-initialized such
that [dcl.init.list] specifies that overload resolution is performed
according to the rules in this section, overload resolution selects
the constructor in two phases:
(1.1) Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T and the argument list
consists of the initializer list as a single argument.
(1.2) If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all
the constructors of the class T and the argument list consists of
the elements of the initializer list.
If the initializer list has no elements and T has a default
constructor, the first phase is omitted. In
copy-list-initialization, if an explicit constructor is chosen, the
initialization is ill-formed. [ Note: This differs from other
situations ([over.match.ctor], [over.match.copy]), where only
converting constructors are considered for copy-initialization. This
restriction only applies if this initialization is part of the final
result of overload resolution.  — end note ]
copy-list-initialization with an empty braced-init-list {} for non-aggregates prohibits use of explicit default constructors; e.g.:
struct Foo {
virtual void notAnAggregate() const {};
explicit Foo() {}
};
void foo(Foo) {}
int main() {
Foo f1{}; // OK: direct-list-initialization
// Error: converting to 'Foo' from initializer
// list would use explicit constructor 'Foo::Foo()'
Foo f2 = {};
foo({});
}
Albeit the standard quote above refers to C++17, this likewise applies for C++11, C++14 and C++20.
Case #2 [C++17 only]: A class type with a user-declared constructor that is marked as explicit is not an aggregate
[dcl.init.aggr]/1 added was updated some between C++14 and C++17, mainly by allowing an aggregate to derive publicly from a base class, with some restrictions, but also prohibiting explicit constructors for aggregates [emphasis mine]:
An aggregate is an array or a class with
(1.1) no user-provided, explicit, or inherited constructors ([class.ctor]),
(1.2) no private or protected non-static data members (Clause [class.access]),
(1.3) no virtual functions, and
(1.4) no virtual, private, or protected base classes ([class.mi]).
As of P1008R1 (Prohibit aggregates with user-declared constructors), which has been implemented for C++20, we may no longer ever declare constructors for aggregates. In C++17 alone, however, we had the peculiar rule that whether a user-declared (but not user-provided) constructor was marked explicit decided whether the class type was an aggregate or not. E.g. the class types
struct Foo {
Foo() = default;
};
struct Bar {
explicit Bar() = default;
};
were aggregates/not aggregates in C++11 through C++20 as follows:
C++11: Foo & Bar are both aggregates
C++14: Foo & Bar are both aggregates
C++17: Only Foo is an aggregate (Bar has an explicit constructor)
C++20: None of Foo or Bar are aggregates (both has user-declared constructors)