Please consider the following code:
template <typename T>
struct Test
{
Test() = default;
explicit Test(const T& arg)
: m_member(arg)
{
}
T m_member{};
};
int main()
{
Test<int> t1;
int v2 = 34;
Test<int&> t2(v2); // (!)
return 0;
}
Should the code above compile and exhibit no undefined behavior?
The line marked (!) instantiates the class template Test with a parameter of reference type. In this case, the default initializer for member Test::m_member is invalid (well, a reference must be initialized with some object). But on the other hand, the default constructor (the only one which could have used that default initializer) is never used in the program, and so it shouldn't be instantiated.
Under C++11, is the compiler allowed/required to attempt/skip the instantiation of a default initializer for a member which is not used in the instantiated constructors (i.e. the program doesn't instantiate any of the constructors which could require that initializer)?
Fortunately, this is OK.
But on the other hand, the default constructor (the only one which could have used that default initializer) is never used in the program, and so it shouldn't be instantiated.
Indeed:
[temp.mem.func]/2
The template-arguments for a member function of a class template are determined by the template-arguments of the type of the object for which the member function is called.
Since in your example, Test<int&>::Test() is not called, it's template-arguments are not determined and its construct doesn't make the program ill-formed.
This is described in CWG Issue 1396
Deferred instantiation and checking of non-static data member initializers
Section: 17.8.1 [temp.inst] Status: drafting Submitter: Jason Merrill Date: 2011-09-22
Non-static data member initializers get the same late parsing as
member functions and default arguments, but are they also instantiated
as needed like them? And when is their validity checked?
Notes from the October, 2012 meeting:
CWG agreed that non-static data member initializers should be handled
like default arguments.
As you see, the consensus was that default member initializers be treated like default arguments to functions. So we can examine the behavior for default arguments to determine how the CWG intends for this to be treated. We can see that those aren't instantiated along with the class definition:
[temp.inst]/1
The implicit instantiation of a class template specialization causes
the implicit instantiation of the declarations, but not of the
definitions or default arguments, of the class member functions,
member classes, scoped member enumerations, static data members and
member templates;
And that they would be instantiated if a constructor was called in a way that used them, same as a member function f in this paragraph
[temp.inst]/12
If a function template f is called in a way that requires a default
argument to be used, the dependent names are looked up, the semantics
constraints are checked, and the instantiation of any template used in
the default argument is done as if the default argument had been an
initializer used in a function template specialization with the same
scope, the same template parameters and the same access as that of the
function template f used at that point. This analysis is called
default argument instantiation. The instantiated default argument is
then used as the argument of f.
Should the code above compile and exhibit no undefined behavior?
Yes, I can't see any reason why it shouldn't.
Under C++11, is the compiler allowed/required to attempt/skip the
instantiation of a default initializer for a member which is not used
in the instantiated constructors (i.e. the program doesn't instantiate
any of the constructors which could require that initializer)?
I don't have the C++11 spec but the current draft says:
[default.ctor-2.3]
A defaulted default constructor for class X is defined as deleted if:
any non-static data member with no default member initializer ([class.mem]) is of reference type,
Since you do have a default member initializer (T m_member{};) it will not delete your defaulted default constructor in case you try to use it when T is a reference type.
It'll instead fail compiling:
All output is from using -std=c++11
g++: error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
clang++ error: non-const lvalue reference to type 'int' cannot bind to an initializer list temporary
Trying to fix it by removing the default member initializer:
11 | T m_member;
Results in the deletion required:
21:16: error: use of deleted function ‘Test<T>::Test() [with T = int&]’
21 | Test<int&> x;
| ^
4:5: note: ‘Test<T>::Test() [with T = int&]’ is implicitly deleted because the default definition would be ill-formed:
4 | Test() = default;
| ^~~~
4:5: error: uninitialized reference member in ‘struct Test<int&>’
Related
I have the code:
class A {
public:
A() = default;
private:
int i = 1;
};
int main() {
const A a;
return 0;
}
It compiles fine on g++ (see ideone), but fails on clang++ with error:
default initialization of an object of const type 'const A' requires a user-provided default constructor
I reported this issue on LLVM bug-tracker and got it INVALID.
I see it absolutly pointless to try to convince the clang developers. On the other side, I don't see the reason for such restriction.
Can anyone advise, if the C++11 Standard somehow implies this code to be invalid? Or should I just report a bug to g++? Or maybe there is enough freedom in language rules to handle this code in many ways?
N3797 §8.5/7 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.
There's no further example or explanation of this. I agree it seems pretty bizarre. Furthermore the rule was updated in C++11 to be more restrictive than it was in C++03, when class types needed user-declared constructors. (Your constructor is user-declared.)
The workaround is be to ask for value initialization using {}, or use Dietmar's clever out-of-class inline definition.
GCC does provide a diagnosis (and quite a nice one, referring to the newer C++11 requirements) if you add another member without an initializer.
private:
int i = 1;
int j;
unmem.cpp:11:11: error: uninitialized const ‘a’ [-fpermissive]
const A a;
^
unmem.cpp:1:7: note: ‘const class A’ has no user-provided default constructor
class A {
^
unmem.cpp:3:5: note: constructor is not user-provided because it is explicitly defaulted in the class body
A() = default;
^
unmem.cpp:7:9: note: and the implicitly-defined constructor does not initialize ‘int A::j’
int j;
The GCC source refers to DR 253, Why must empty or fully-initialized const objects be initialized? This is an open issue in the standard, last updated in August 2011 (post-C++11) with this note:
If the implicit default constructor initializes all subobjects, no initializer should be required.
Therefore whereas Clang complies with C++11 (and will comply as-is with C++14), GCC is implementing the latest thinking of the standardization committee.
Filed a GCC bug. I predict that you'll need -pedantic to get a diagnosis when (and if) the bug is fixed.
Note that you can turn your class easily into one which has a user-defined default constructor:
class A {
public:
A();
private:
int i = 1;
};
inline A::A() = default;
According to 8.4.2 [dcl.fct.def.default] paragraph 4:
... A special member function is user-provided if it is user-declared and not explicitly
defaulted or deleted on its first declaration. ...
This implicitly states that a function which is not explicitly defaulted on its first declaration is not user-provided. In combination with 8.5 [dcl.init] paragraph 6
... 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.
it seems clear that you cannot use a default constructor defaulted on its first declaration to initialize a const object. However, you can use a defaulted definition if it isn't the first declaration as is done in the code above.
Edit: The following is based on outdated information. I just went through N3797 and this is what I found:
§ 8.5/7 [dcl.init]
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.
Note the standard quote in the link below says user-declared.
The following program compiles in g++ but not clang++:
struct A {};
void f()
{
A const a;
}
And it might be related to this bug report where it was "fixed". g++ fails to compile it once it contains data members unless they're initialized. Note that int member = 1 will no longer make A a POD. Comparatively, clang++ rejects all permutations (empty classes and data members initialized or not.) For an interpretation of what the standard means by the following paragraph:
§ 8.5/9 [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.
See Why does C++ require a user-provided default constructor to default-construct a const object?. Supposedly the program is ill-formed if the object is of const-qualified POD type, and there is no initializer specified (because POD are not default initialized). Note how g++ behaves for the following:
struct A {int a;};
struct B {int a = 1;};
int main()
{
A a;
B b;
const A c; // A is POD, error
const B d; // B is not POD, contains data member initializer, no error
}
Since C++17, this code is correct, as is the similar code from this question:
struct MyClass1 { int i{}; };
struct MyClass2 { const MyClass1 m; };
MyClass2 a;
clang 8.0.0 rejects this latter code even with -std=c++17 which means that clang 8.0.0 has a bug.
In C++17 the following new text was added as [dcl.init]/7 (as per P0490R0 in response to DR 253):
A class type T is const-default-constructible if default-initialization of T would invoke a user-provided constructor of T (not inherited from a base class) or if
each direct non-variant non-static data member M of T has a default member initializer or, if M is of class type X (or array thereof), X is const-default-constructible,
if T is a union with at least one non-static data member, exactly one variant member has a default member initializer,
if T is not a union, for each anonymous union member with at least one non-static data member, exactly one non-static data member has a default member initializer, and
each potentially constructed base class of T is const-default-constructible.
If a program calls for the default-initialization of an object of a const-qualified type T , T shall be a const-default-constructible class type or array thereof.
Prior to C++17 there was no such text; an object defined as const must either have an initializer or a user-provided constructor. So, prior to C++17, clang was correct and g++ was bugged to accept the code without diagnostic.
I just stumbled upon the following differences between GCC and Clang concerning an explicitly defaulted constexpr ctor and some inheritance...
template <typename T>
struct A {
constexpr A() = default;
T v;
};
struct B : A<int> {
constexpr B() = default;
};
GCC immediately rejects the code while Clang allows to instantiate non-constexpr versions of both types. My guess is that Clang is probably correct, but I'm not 100% certain...
The problem boils down to:
is a constexpr constructor that default-initializes
some non-static data member of builtin type valid,
if it is not used?
tl;dr:
For a non-template constructor,
no, it is invalid to leave any non-static data members uninitialized.
For a template constructor, yes,
it is valid to have some (but not all, no diagnostic required)
instantiated template specializations
for which the instantiated constructor does not meet the requirements
of a constexpr constructor.
In this case, GCC is right, whereas Clang is wrong.
GCC gives the following error message which is very informative:
prog.cc:8:13: error: explicitly defaulted function 'constexpr B::B()' cannot be declared as 'constexpr' because the implicit declaration is not 'constexpr':
8 | constexpr B() = default;
| ^
prog.cc:3:13: note: defaulted constructor calls non-'constexpr' 'A<T>::A() [with T = int]'
3 | constexpr A() = default;
| ^
prog.cc:3:13: note: 'A<T>::A() [with T = int]' is not usable as a 'constexpr' function because:
prog.cc:4:5: note: defaulted default constructor does not initialize 'int A<int>::v'
4 | T v;
| ^
live demo
Note that the error is raised on the constructor of B,
instead of that of A,
whose constructor is merely "not usable as a constexpr function
because [the] defaulted default constructor
does not initialize int A<int>::v."
Per [dcl.constexpr]/4:
The definition of a constexpr constructor shall satisfy the following
requirements:
the class shall not have any virtual base classes;
each of the parameter types shall be a literal type.
In addition, either its function-body shall be = delete, or it
shall satisfy the following requirements:
[...]
every non-variant non-static data member and base class subobject
shall be initialized ([class.base.init]);
[...]
Here, v is of type int, and is not initialized.
Therefore, it seems that the constructor of A
cannot be declared constexpr.
However, [dcl.constructor]/6 says:
If the instantiated template specialization of a constexpr function
template or member function of a class template would fail to satisfy
the requirements for a constexpr function or constexpr constructor,
that specialization is still a constexpr function or constexpr
constructor, even though a call to such a function cannot appear in a
constant expression. If no specialization of the template would
satisfy the requirements for a constexpr function or constexpr
constructor when considered as a non-template function or constructor,
the template is ill-formed, no diagnostic required.
Therefore, the constructor of A that is declared constexpr
is actually valid,
even when instantiated for T = int!
The problem is the constructor of B.
B is an ordinary class (as opposed to a class template),
and for its constructor to be (merely) declared constexpr,
A<int> must have a constexpr constructor,
which is not the case.
Therefore, this code should be rejected, as GCC does.
(Note that both compilers reject initialization of such a type,
for example:
A a{};
B b{};
The above code is rejected by both compilers.)
As mentioned in a comment,
initialize A::v and GCC (and the standard) will be happy.
C++17 introduces template argument deduction.
With gcc-7.2, I can use it easily in a function:
int test() {
std::pair d(0, 0.0);
}
I was expecting this same syntax to work in class non-static data members, like:
class Test {
std::pair d_{0, 0.0};
};
but this causes gcc error: invalid use of template-name ... without an argument list, with --std=c++17 passed.
I tried a few other combinations, but none seems to work.
Is this the intended behavior by the standard, or is this a case of incomplete support by the compiler? I can't find any explicit reference to class data members in the standard.
My use case is of course much more complex, having this syntax would be extremely convenient (think functions being passed and stored).
Is this the intended behavior by the standard, or is this a case of incomplete support by the compiler?
Yes, this is intended behavior. [dcl.type.class.deduct] reads:
If a placeholder for a deduced class type appears as a decl-specifier in the decl-specifier-seq of an initializing declaration ([dcl.init]) of a variable, [...]
A placeholder for a deduced class type can also be used in the type-specifier-seq in the new-type-id or type-id of a new-expression, or as the simple-type-specifier in an explicit type conversion (functional notation). A placeholder for a deduced class type shall not appear in any other context.
A non-static data member is not a variable, and we're in none of the other situations.
Note that the same principle is true for non-static data members attempting to be declared with auto:
struct X {
auto y = 0; // error
};
The default member initializer is just that - a default initializer. What if you provided a constructor that initialized the member with expression(s) of different types?
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 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);
Consider the following:
#include <iostream>
template <typename T>
struct Foo
{
Foo (T v = {}) : var (v) {}
T var;
};
int main()
{
// Foo<int&> f; // cannot compile
int x = 42;
Foo<int&> f(x);
std::cout << f.var;
}
It builds and runs successfully in GCC 4.8, but is it strictly legal?
T v = {} is invalid for T=int&, but this default argument is not used.
The only pertinent language I can find in the standard does not seem to explicitly explain whether or not this program is valid; none of the following really seems completely relevant, despite touching on the subject in various ways:
[C++11: 8.3.6/1]: If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument. Default arguments will be used in calls where trailing arguments are missing.
[C++11: 8.3.6/5]: A default argument is implicitly converted (Clause 4) to the parameter type. The default argument has the same semantic constraints as the initializer in a declaration of a variable of the parameter type, using the copy-initialization semantics (8.5). The names in the default argument are bound, and the semantic constraints are checked, at the point where the default argument appears. Name lookup and checking of semantic constraints for default arguments in function templates and in member functions of class templates are performed as described in 14.7.1. [..]
[C++11: 8.3.6/9]: Default arguments are evaluated each time the function is called. [..]
See 14.7.1 Implicit instantiation[temp.inst]/13:
If a function template f is called in a way that requires a default
argument to be used, the dependent names are looked up, the semantics
constraints are checked, and the instantiation of any template used in
the default argument is done as if the default argument had been an
initializer used in a function template specialization with the same
scope, the same template parameters and the same access as that of the
function template f used at that point, except that the scope in which
a closure type is declared (5.1.2) – and therefore its associated
namespaces – remain as determined from the context of the definition
for the default argument. This analysis is called default argument
instantiation. The instantiated default argument is then used as the
argument of f.
The example below it also shows a case where the default argument is ill-formed
if instantiated:
template<class T> void f(T x, T y = ydef(T()), T z = zdef(T()));
class
A { };
A zdef(A);
void g(A a, A b, A c) {
f(a, b, c); // no default argument instantiation
f(a, b); // default argument z = zdef(T()) instantiated
f(a); // ill-formed; ydef is not declared
}
There is no ydef, so the calls that use it are ill-formed, but the calls that don't use it are ok.
C++11, 14.7.1/3:
... Unless a call is to a function template explicit specialization or to a
member function of an explicitly specialized class template, a default argument for a function template or a
member function of a class template is implicitly instantiated when the function is called in a context that
requires the value of the default argument.
(Emphasis mine).
Combine this with 14.7.1/1:
... The implicit instantiation of a class template specialization causes the implicit
instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, ...
(Emphasis mine again)
I would say that from the above, it follows that when called so that the default argument value is not required, it is not instantiated.