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.
Related
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.
I'm migrating a C++ Visual Studio Project from VS2017 to VS2019.
I'm getting an error now, that didn't occur before, that can be reproduced with these few lines of code:
struct Foo
{
Foo() = default;
int bar;
};
auto test = Foo { 0 };
The error is
(6): error C2440: 'initializing': cannot convert from
'initializer list' to 'Foo'
(6): note: No constructor could take the source type, or
constructor overload resolution was ambiguous
The project is compiled with /std:c++latest flag. I reproduced it on godbolt. If I switch it to /std:c++17, it compiles fine as before.
I tried to compile the same code with clang with -std=c++2a and got a similar error. Also, defaulting or deleting other constructors generates this error.
Apparently, some new C++20 features were added in VS2019 and I'm assuming the origin of this issue is described in https://en.cppreference.com/w/cpp/language/aggregate_initialization.
There it says that an aggregate can be a struct that (among other criteria) has
no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed) (since C++17) (until C++20)
no user-declared or inherited constructors (since C++20)
Note that the part in parentheses "explicitly defaulted or deleted constructors are allowed" was dropped and that "user-provided" changed to "user-declared".
So my first question is, am I right assuming that this change in the standard is the reason why my code compiled before but does not anymore?
Of course, it's easy to fix this: Just remove the explicitly defaulted constructors.
However, I have explicitly defaulted and deleted very many constructors in all of my projects because I found it was a good habit to make code much more expressive this way because it simply results in fewer surprises than with implicitly defaulted or deleted constructors. With this change however, this doesn't seem like such a good habit anymore...
So my actual question is:
What is the reasoning behind this change from C++17 to C++20? Was this break of backwards compatibility made on purpose? Was there some trade off like "Ok, we're breaking backwards compatibility here, but it's for the greater good."? What is this greater good?
The abstract from P1008, the proposal that led to the change:
C++ currently allows some types with user-declared constructors to be initialized via aggregate initialization, bypassing those constructors. The result is code that is surprising, confusing, and buggy. This paper proposes a fix that makes initialization semantics in C++ safer, more uniform,and easier to teach. We also discuss the breaking changes that this fix introduces.
One of the examples they give is the following.
struct X {
int i{4};
X() = default;
};
int main() {
X x1(3); // ill-formed - no matching c’tor
X x2{3}; // compiles!
}
To me, it's quite clear that the proposed changes are worth the backwards-incompatibility they bear. And indeed, it doesn't seem to be good practice anymore to = default aggregate default constructors.
The reasoning from P1008 (PDF) can be best understood from two directions:
If you sat a relatively new C++ programmer down in front of a class definition and ask "is this an aggregate", would they be correct?
The common conception of an aggregate is "a class with no constructors". If Typename() = default; is in a class definition, most people will see that as having a constructor. It will behave like the standard default constructor, but the type still has one. That is the broad conception of the idea from many users.
An aggregate is supposed to be a class of pure data, able to have any member assume any value it is given. From that perspective, you have no business giving it constructors of any kind, even if you defaulted them. Which brings us to the next reasoning:
If my class fulfills the requirements of an aggregate, but I don't want it to be an aggregate, how do I do that?
The most obvious answer would be to = default the default constructor, because I'm probably someone from group #1. Obviously, that doesn't work.
Pre-C++20, your options are to give the class some other constructor or to implement one of the special member functions. Neither of these options are palatable, because (by definition) it's not something you actually need to implement; you're just doing it to make some side effect happen.
Post-C++20, the obvious answer works.
By changing the rules in such a way, it makes the difference between an aggregate and non-aggregate visible. Aggregates have no constructors; so if you want a type to be an aggregate, you don't give it constructors.
Oh, and here's a fun fact: pre-C++20, this is an aggregate:
class Agg
{
Agg() = default;
};
Note that the defaulted constructor is private, so only people with private access to Agg can call it... unless they use Agg{}, bypasses the constructor and is perfectly legal.
The clear intent of this class is to create a class which can be copied around, but can only get its initial construction from those with private access. This allows forwarding of access controls, as only code which was given an Agg can call functions that take Agg as a parameter. And only code with access to Agg can create one.
Or at least, that's how it is supposed to be.
Now you could fix this more targetedly by saying that it's an aggregate if the defaulted/deleted constructors are not publicly declared. But that feels even more in-congruent; sometimes, a class with a visibly declared constructor is an aggregate and sometimes it isn't, depending on where that visibly declared constructor is.
Towards a less surprising aggregate in C++20
To be on the same page with all readers, lets start by mentioning that aggregate class types make up a special family of class types that can be, particularly, initialized by means of aggregate initialization, using direct-list-init or copy-list-init, T aggr_obj{arg1, arg2, ...} and T aggr_obj = {arg1, arg2, ...}, respectively.
The rules governing whether a class is an aggregate or not are not entirely straight-forward, particularly as the rules have been changing between different releases of the C++ standard. In this post we’ll go over these rules and how they have changed over the standard release from C++11 through C++20.
Before we visit the relevant standard passages, consider the implementation of the following contrived class type:
namespace detail {
template <int N>
struct NumberImpl final {
const int value{N};
// Factory method for NumberImpl<N> wrapping non-type
// template parameter 'N' as data member 'value'.
static const NumberImpl& get() {
static constexpr NumberImpl number{};
return number;
}
private:
NumberImpl() = default;
NumberImpl(int) = delete;
NumberImpl(const NumberImpl&) = delete;
NumberImpl(NumberImpl&&) = delete;
NumberImpl& operator=(const NumberImpl&) = delete;
NumberImpl& operator=(NumberImpl&&) = delete;
};
} // namespace detail
// Intended public API.
template <int N>
using Number = detail::NumberImpl<N>;
where the design intent has been to create a non-copyable, non-movable singleton class template which wraps its single non-type template parameter into a public constant data member, and where the singleton object for each instantiation is the only that can ever be created for this particular class specialization. The author has defined an alias template Number solely to prohibit users of the API to explicitly specialize the underlying detail::NumberImpl class template.
Ignoring the actual usefulness (or, rather, uselessness) of this class template, have the author correctly implemented its design intent? Or, in other words, given the function wrappedValueIsN below, used as an acceptance test for the design of the publicly intended Number alias template, will the function always return true?
template <int N>
bool wrappedValueIsN(const Number<N>& num) {
// Always 'true', by design of the 'NumberImpl' class?
return N == num.value;
}
We will answer this question assuming that no user abuses the interface by specializing the semantically hidden detail::NumberImpl, in which case the answer is:
C++11: Yes
C++14: No
C++17: No
C++20: Yes
The key difference is that the class template detail::NumberImpl (for any non-explicit specialization of it) is an aggregate in C++14 and C++17, whereas it is not an aggregate in C++11 and C++20. As covered above, initialization of an object using direct-list-init or copy-list-init will result in aggregate initialization if the object is of an aggregate type. Thus, what may look like value-initialization (e.g. Number<1> n{} here)—which we may expect will have the effect of zero-initialization followed by default-initialization as a user-declared but not user-provided default constructer exists—or direct-initialization (e.g. Number<1>n{2} here) of a class type object will actually bypass any constructors, even deleted ones, if the class type is an aggregate.
struct NonConstructible {
NonConstructible() = delete;
NonConstructible(const NonConstructible&) = delete;
NonConstructible(NonConstructible&&) = delete;
};
int main() {
//NonConstructible nc; // error: call to deleted constructor
// Aggregate initialization (and thus accepted) in
// C++11, C++14 and C++17.
// Rejected in C++20 (error: call to deleted constructor).
NonConstructible nc{};
}
Thus, we can fail the wrappedValueIsN acceptance test in C++14 and C++17 by bypassing the private and deleted user-declared constructors of detail::NumberImpl by means of aggregate initialization, specifically where we explicitly provide a value for the single value member thus overriding the designated member initializer (... value{N};) that otherwise sets its value to N.
constexpr bool expected_result{true};
const bool actual_result =
wrappedValueIsN(Number<42>{41}); // false
// ^^^^ aggr. init. int C++14 and C++17.
Note that even if detail::NumberImpl were to declare a private and explicitly defaulted destructor (~NumberImpl() = default; with private access specifyer) we could still, at the cost of a memory leak, break the acceptance test by e.g. dynamically allocating (and never deleting) a detail::NumberImpl object using aggregate initialization (wrappedValueIsN(*(new Number<42>{41}))).
But why is detail::NumberImpl an aggregate in C++14 and C++17, and why is it not an aggregate in C++11 and C++20? We shall turn to the relevant standard passages for the different standard versions for an answer.
Aggregates in C++11
The rules governing whether a class is an aggregate or not is covered by [dcl.init.aggr]/1, where we refer to N3337 (C++11 + editorial fixes) for C++11 [emphasis mine]:
An aggregate is an array or a class (Clause [class]) with no
user-provided constructors ([class.ctor]), no
brace-or-equal-initializers for non-static data members
([class.mem]), no private or protected non-static data members (Clause
[class.access]), no base classes (Clause [class.derived]), and no
virtual functions ([class.virtual]).
The emphasized segments are the most relevant ones for the context of this answer.
User-provided functions
The detail::NumberImpl class does declare four constructors, such that it has four user-declared constructors, but it does not provide definitions for any of these constructors; it makes use of explicitly-defaulted and explicitly-deleted function definitions at the constructors’ first declarations, using the default and delete keywords, respectively.
As governed by [dcl.fct.def.default]/4, defining an explicitly-defaulted or explicitly-deleted function at its first declaration does not count as the function being user-provided [extract, emphasis mine]:
[…] A special member function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. […]
Thus, the detail::NumberImpl fulfills the aggregate class requirement regarding having no user-provided constructors.
For the some additional aggregate confusion (which applies in C++11 through C++17), where the explicitly-defaulted definition is provided out-of-line, refer to my other answer here.
Designated member initializers
Albeit the detail::NumberImpl class has no user-provided constructors, it does use a brace-or-equal-initializer (commmonly referred to as a designated member initializer) for the single non-static data member value. This is the sole reason as to why the detail::NumberImpl class is not an aggregate in C++11.
Aggregates in C++14
For C++14, we once again turn to [dcl.init.aggr]/1, now referring to N4140 (C++14 + editorial fixes), which is nearly identical to the corresponding paragraph in C++11, except that the segment regarding brace-or-equal-initializers has been removed [emphasis mine]:
An aggregate is an array or a class (Clause [class]) with no
user-provided constructors ([class.ctor]), no private or protected
non-static data members (Clause [class.access]), no base classes
(Clause [class.derived]), and no virtual functions ([class.virtual]).
Thus, the detail::NumberImpl class fulfills the rules for it to be an aggregate in C++14, thus allowing circumventing all private, defaulted or deleted user-declared constructors by means of aggregate initialization.
We will get back to the consistently emphasized segment regarding user-provided constructors once we reach C++20 in a minute, but we shall first visit some explicit puzzlement in C++17.
Aggregates in C++17
True to its form, the aggregate once again changed in C++17, now allowing an aggregate to derive publicly from a base class, with some restrictions, as well as prohibiting explicit constructors for aggregates. [dcl.init.aggr]/1 from N4659 ((March 2017 post-Kona working draft/C++17 DIS), states [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]).
The segment in about explicit is interesting in the context of this post, as we may further increase the aggregate cross-standard-releases volatility by changing the declaration of the private user-declared explicitly-defaulted default constructor of detail::NumberImpl from:
template <int N>
struct NumberImpl final {
// ...
private:
NumberImpl() = default;
// ...
};
to
template <int N>
struct NumberImpl final {
// ...
private:
explicit NumberImpl() = default;
// ...
};
with the effect that detail::NumberImpl is no longer an aggregate in C++17, whilst still being an aggregate in C++14. Denote this example as (*). Apart from copy-list-initialization with an empty braced-init-list (see more details in my other answer here):
struct Foo {
virtual void fooIsNeverAnAggregate() 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({});
}
the case shown in (*) is the only situation where explicit actually has an effect on a default constructor with no parameters.
Aggregates in C++20
As of C++20, particularly due to the implementation of P1008R1 (Prohibit aggregates with user-declared constructors) most of the frequently surprising aggregate behaviour covered above has been addressed, specifically by no longer allowing aggregates to have user-declared constructors, a stricter requirement for a class to be an aggregate than just prohibiting user-provided constructors. We once again turn to [dcl.init.aggr]/1, now referring to N4861 (March 2020 post-Prague working draft/C++20 DIS), which states [emphasis mine]:
An aggregate is an array or a class ([class]) with
(1.1) no user-declared, or inherited constructors ([class.ctor]),
(1.2) no private or protected non-static data members ([class.access]),
(1.3) no virtual functions ([class.virtual]), and
(1.4) no virtual, private, or protected base classes ([class.mi]).
We may also note that the segment about explicit constructors has been removed, now redundant as we cannot mark a constructor as explicit if we may not even declare it.
Avoiding aggregate surprises
All the examples above relied on class types with public non-static data members, which is commonly considered an anti-pattern for the design of “non-POD-like” classes. As a rule of thumb, if you’d like to avoid designing a class that is unintentionally an aggregate, simply make sure that at least one (typically even all) of its non-static data members is private (/protected). For cases where this for some reason cannot be applied, and where you still don’t want the class to be an aggregate, make sure to turn to the relevant rules for the respective standard (as listed above) to avoid writing a class that is not portable w.r.t. being an aggregate or not over different C++ standard versions.
Actually, MSDN addressed your concern in the below document:
Modified specification of aggregate type
In Visual Studio 2019, under /std:c++latest, a class with any user-declared constructor (for example, including a constructor declared = default or = delete) isn't an aggregate. Previously, only user-provided constructors would disqualify a class from being an aggregate. This change puts additional restrictions on how such types can be initialized.
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.
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.
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)