Context: Embedded programming and initializer list nuances, especially the one that I think should invoke the copy constructor.
Question: Will the instantiation of a class in another class's initializer list to initialize a value-held member variable invoke the copy constructor?
Example provided below where TestClassCopy has a member variable TestMember held by value as opposed to pointer or reference. This cppreference page does not seem to cover this sufficiently in the examples provided.
Bonus question: Will a copy constructor invoked in an initializer list produce any time/space perf impact? Seems like a compiler should be able to optimize this away if the C++ spec allows.
Here's the code (build tested with the VS2015 toolchain):
TestMember.h (not showing TestMember.cpp for space reasons)
#pragma once
#include <stdint.h>
class TestMember {
public:
TestMember(uint8_t);
private:
uint8_t m_value;
};
TestClassCopy.h
#pragma once
#include "test_member.h"
class TestClassCopy {
public:
TestClassCopy();
virtual ~TestClassCopy();
private:
TestMember m_member;
};
TestClassCopy.cpp
#include "test_class_copy.h"
TestClassCopy::TestClassCopy() :
m_member(TestMember(255)) { // invokes copy constructor yes?
}
TestClassCopy::~TestClassCopy() {
}
For completeness, other things where I might be making assumptions I shouldn't:
For a member pointer to a TestMember, a 'new' in the initializer list and a 'delete' in the destructor should be sufficient.
For a member reference, my understanding is that there's even more nuance in that if the reference is passed into the constructor, you can just assign it (since lifetime is managed outside of the initializer list). However, if the TestMember is instantiated in the initializer list (into a reference), then that's a no-no since the TestMember temporary goes away after the initialization is complete.
TestMemberReference.h
#pragma once
class TestMember;
class TestClassReference {
public:
TestClassReference();
virtual ~TestClassReference();
private:
TestMember& m_member;
};
TestMemberReference.cpp
#include "test_class_reference.h"
#include "test_member.h"
TestClassReference::TestClassReference() :
m_member(TestMember(255)) { // ew, don't do this; TestMember temporary will go out of scope
}
TestClassReference::~TestClassReference() {
}
This cppreference page does not seem to cover this sufficiently in the examples provided
that cppreference page says "Initializes the base or member named by class-or-identifier using direct initialization" and if you click that, you can read that, as of C++17,
if the initializer is a prvalue expression whose cv-unqualified type is the same class as T, the initializer expression itself, rather that a temporary materialized from it, is used to initialize the destination object: see copy elision
so the answer is: in C++17 no copy constructor is called. Before C++17, a copy constructor could technically be called, but copy elision eliminated that call. You could still observe it, as proof, by compiling your example with g++ -fno-elide-constructors -O0 -std=c++14
Related
Inspired by my (currently deleted) answer to this question (but there's a summary in my comment thereto), I was wondering whether the constructor for the Derived class in the code below exhibits undefined behaviour.
#include <iostream>
class Base {
public:
Base(int test) {
std::cout << "Base constructor, test: " << test << std::endl;
}
};
class Derived : public Base {
private:
int variable;
public: Derived() : Base(variable = 50) { // Is this undefined behaviour?
}
};
int main()
{
Derived derived;
return 0;
}
I know that, when the base c'tor is called, the derived object has not yet (formally) been constructed, so the lifetime of the variable member has not yet started. However, this excerpt from the C++ Standard (thanks to NathanOliver for the reference) suggests (maybe) that the object may be used "in limited ways" (bolding mine):
7 Similarly, before the lifetime of an object has started
but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before
the storage which the object occupied is reused or released, any
glvalue that refers to the original object may be used but only in
limited ways. For an object under construction or destruction, see
[class.cdtor]. Otherwise, such a glvalue refers to allocated storage
([basic.stc.dynamic.allocation]), and using the properties of the
glvalue that do not depend on its value is well-defined. …
Clearly, if variable were a object which itself had a non-trivial constructor, there would (almost certainly) be undefined behaviour here. However, for a primitive (or POD) type like an int, can we assume that the storage for it has been allocated? And, if so, does the last phrase of the above quote hold, or is this still UB?
As an aside, neither clang-cl nor MSVC, even with full warnings enabled, give any diagnostic for the code shown, and it runs as expected. However, I appreciate that neither of those tools qualify as a formal interpretation/enforcement of the C++ Standard.
The behavior is undefined, regardless of whether or not the Base constructor accepts the parameter by reference.
When control reaches Base(variable = 50), the lifetime of variable hasn't started yet, because data members are initialized after base classes.
So first, writing to it causes UB because the lifetime hasn't started yet. Then, because you pass by value, reading from it is also UB.
[class.base.init]/13
In a non-delegating constructor, initialization proceeds in the following order:
— First, and only for the constructor of the most derived class ..., virtual base classes are initialized ...
— Then, direct base classes are initialized ...
— Then, non-static data members are initialized in the order they were declared in the class definition ...
— Finally, the ... the constructor body is executed.
Idea by #Jarod42: as an experiment, you can try this in a constexpr context, which is supposed to catch UB.
#include <type_traits>
#include <iostream>
struct Base
{
int x;
constexpr Base(int x) : x(x) {}
};
struct Derived : Base
{
int variable;
constexpr Derived() : Base(variable = 42) {}
};
constexpr Derived derived;
Clang rejects this with:
error: constexpr variable 'derived' must be initialized by a constant expression
note: assignment to object outside its lifetime is not allowed in a constant expression
while GCC and MSVC accept it.
My question is about the following code:
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;
class Blob {
public:
int age;
void hello() { printf("hello\n"); }
};
void test_case() {
Blob a;
//a.hello(); //(1)
cout << a.age << endl;
}
int main() {
test_case();
return 0;
}
if I comment out (1), it compile success. If uncomment out (1), compile error occur, e.g. in VS2017 it complains "using an unintialized local variable 'a'".
I've searched in search engine for a while and now only know the following 4 cases that the compiler will automatically help me define the default constructor:
a class member is an instance of a class (say B), and class B have defined default constructor
a class is derived from another class (say B), and B has defined default constructor
a class has virtual function
any combination of the previous 3 cases.
I'm curious that, if I comment out (1), will the compiler add a definition of constructor for the class Blob?
In fact it has nothing to do with comment out or not a.hello();, Blob always has a generated default constructor, otherwise Blob a; won't compile.
(emphasis mine)
If no user-declared constructors of any kind are provided for a class
type (struct, class, or union), the compiler will always declare a
default constructor as an inline public member of its class.
If the implicitly-declared default constructor is not defined as
deleted, it is defined (that is, a function body is generated and
compiled) by the compiler if odr-used, and it has the same effect as a
user-defined constructor with empty body and empty initializer list.
As the result, a.age is default-initialized to indeterminate value, any access to it (like cout << a.age;) leads to UB.
3) when a base class or a non-static data member is not mentioned in a
constructor initializer list and that constructor is called.
otherwise, nothing is done: the objects with automatic storage
duration (and their subobjects) are initialized to indeterminate
values.
It depends on your intent; as the workaround you can add a user-defined default constructor.
class Blob {
public:
int age;
void hello() { printf("hello\n"); }
Blob() : age(42) {}
};
There are 2 points here.
First is the Undefined Behaviour question. As you do not initialize age, it contains an indeterminate value which leads to UB if you use it (see songyuanyao's answers for more details on that). Adding an additional instruction does not change anything to that point.
Next is the compiler message. Compilers are not required to issue any warning facing UB. Yours is not specially consistent if it raises the error in only one case, but the programmer is supposed to never write UB. So you cannot really blame the compiler for not issuing the warning.
TL/DR: do not expect a C++ compiler to alway issue warning when you write incorrect code. Having no warning is a necessary condition, but not a sufficient one.
How can I prohibit the construction of an object? I mark = delete; all relevant special functions as follows:
struct A
{
A() = delete;
A(A const &) = delete;
A(A &&) = delete;
void * operator new(std::size_t) = delete;
void operator delete(void *) = delete;
};
A x{};
A y = {};
A * z = ::new A{};
LIVE EXAMPLE
But x, y and *z can still exist. What to do? I am interested in both cases; static/stack allocation and heap allocation.
One option would be to give the class a pure virtual function, and mark it final:
struct A final
{
virtual void nonconstructible() = 0;
};
[Live example]
If you want to have just static members, then write namespace A rather than struct A. Ensuing code will be syntactically similar.
To prevent creation of an instance of a class, make it abstract. (Include one pure virtual function). But doing this introduces a v-table into you class, which you might not want.
If you want to make it impossible to instantiate the class you could just declare private constructors:
class NotInstantiable {
private:
NotInstatiable();
public:
};
And not defining NotInstantiable further. This can't now be instantiated since first the constructor is private but also that a definition for the constructor has not been provided.
The second obstacle for instantiate the NotInstantiable would for example prohibit this possibility, which in fact otherwise is a well known pattern:
class NotInstantiable {
private:
NotInstantiable();
public:
NotInstantiable* evil_method()
{
return new NotInstantiable(); // this will fail if there's no body of the constructor.
}
};
In general, to completely prevent client code instantiation of a class you can declare the class final and either
make the constructors non-public, or
delete the constructors and make sure that the class isn't an aggregate, or
add a pure virtual member function (e.g. make the destructor pure virtual) to make the class abstract.
Declaring the class final is necessary when the non-public is protected, and for the abstract class, in order to prevent instantiation of a base class sub-object of a derived class.
To partially prohibit instantiation, you can
make the destructor non-public.
This prevents automatic and static variables, but it does not prevent dynamic allocation with new.
make the class' allocation function (the operator new) non-public.
This prevents dynamic allocation via an ordinary new-expression in client code, but it does not provide automatic and static variables, or sub-objects of other objects, and it does not prevent dynamic allocation via a ::new-expression, which uses the global allocation function.
There are also other relevant techniques, such as an allocation function with extra arguments that make new-expressions inordinately complicated and impractical. I used that once to force the use of a special macro to dynamically allocate objects, e.g. for a shared-from-this class. But that was in the time before C++11 support for forwarding of arguments; nowadays an ordinary function can do the job, and such a function can be made a friend of the class.
The fact that the code compiles with at least one version of the clang compiler with -std=gnu++1z, is due to a bug and/or language extension in that compiler.
The code should not compile, since it invokes the default constructor that has been deleted. And it does not compile with e.g. MinGW g++ 5.1.0, even with -std=gnu++1z.
The fact that the code compiles with at least one version of the clang compiler with -std=gnu++1z, may be due to a bug and/or language extension in that compiler. What the correct behavior is, is unclear because
Although the code compiles with clang and with Visual C++ 2015, it does not compile with e.g. MinGW g++ 5.1.0, even with -std=gnu++1z.
Intuitively the delete would be meaningless if the code should compile, but many meaningless constructs are permitted in C++.
At issue is whether the class is an aggregate (in which case the new expression performs aggregate initialization), which rests on whether the deleted default constructor can be regarded as user-provided. And as user TartanLlama explains in comments, the requirements for user-provided are
C++11 §8.4.2/4
” A special member function is user-provided if it is user-declared and not explicitly
defaulted or deleted on its first declaration.
I.e. although the delete of the default constructor in this question's example declares that constructor, it's not user-provided (and ditto for the other members) and so the class is an aggregate.
The only defect report I can find about this wording is DR 1355, which however just concerns an issue with the use of the words “special member”, and proposes to drop those words. But, considering both the effect demonstrated by this question, and considering that a function can only be deleted on its first declaration, the wording is strange.
Summing up, formally, as of C++11 (I haven't checked C++14), the code should compile. But this may be a defect in the standard, with the wording not reflecting the intent. And since MinGW g++ 5.1.0 doesn't compile the code, as of October 2015 it's not a good idea to rely on the code compiling.
Essentially this compiles and is allowed because the type A is an aggregate type and the aggregate initialisation doesn't use default constructors.
What is an aggregate type?;
class type (typically, struct or union), that has
no private or protected members
no user-provided constructors (explicitly defaulted or deleted constructors are allowed) (since C++11)
no base classes
no virtual member functions
Giving it any one of the above would make it non-aggregate and thus the aggregate initialisation would not apply. Giving it a private user defined (and unimplemented) constructor will do.
struct A
{
A() = delete;
A(A const &) = delete;
A(A &&) = delete;
void * operator new(std::size_t) = delete;
void operator delete(void *) = delete;
private:
A(int);
};
As a side note; I hope this is a defect in the language specifications. At first look I thought that this should not compile, yet it does. One of the motivations for the =delete was to avoid the C++03 "trick" of declaring the constructors private to "hide" them and thus be unusable. I would expect a =delete on the default constructor to effectively prohibit class creation (outside other user defined constructors).
For easier reading and clearer intent, consider even an empty base class;
struct NonAggregate{};
struct A : private NonAggregate
{
//...
Maybe the simplest yet is to return to the C++03 style here, make the default constructor private;
struct A
{
private:
A(); // note no =delete...
};
In updating some code to use uniform initialization, I thought it would be a drop-in modern substitue for the now "old style" parentheses style. I know this isn't always the case (obvious example, vector<int>) but I've stumbled over another difference that I don't understand.
class Object {
public:
Object() = default;
Object(const Object&) = default;
};
int main() {
Object o;
Object copy{o}; // error
Object copy2(o); // OK
}
fails to compile under clang3.5 with the error: (also fails under gcc)
error: excess elements in struct initializer
There are two different changes to Object that make this work. Either adding a data member to it, or giving it an empty copy constructor body
class Object {
private:
int i; // this fixes it
public:
Object() = default;
Object(const Object&) { } // and/or this fixes it as well
};
I don't see why these should make a difference.
This is a known bug and will hopefully be fixed in C++17 (not for C++14, see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1467). Your struct is an aggregate, so to initialize it with {someElement} there needs to be at least one data member, as you have discovered. Try providing an operator int(); and you will see it compiles.
Johannes's answer is useful, but let me elaborate on why it currently happens.
Both of the changes you describe affect your class by making it go from an aggregate to not an aggregate. See C++11 (N3485) § 8.5.1/1:
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal-initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
A constructor with a definition of = default is considered not user-defined.
Then, going down to list-initialization in § 8.5.4, we see:
List-initialization of an object or reference of type T is defined as follows:
If T is an aggregate, aggregate initialization is performed
And then a bunch of "Otherwise..." sections. Thus, changing either of these allows a constructor to be called instead of performing aggregate initialization.
The new proposed standardese for the list-initialization definition (as viewable in Johannes's link) provides a prioritized case of a single element in the list, and it having a type of (or really close to) the type of the object being initialized. Aggregate initialization would then be top priority after that.
In updating some code to use uniform initialization, I thought it would be a drop-in modern substitue for the now "old style" parentheses style. I know this isn't always the case (obvious example, vector<int>) but I've stumbled over another difference that I don't understand.
class Object {
public:
Object() = default;
Object(const Object&) = default;
};
int main() {
Object o;
Object copy{o}; // error
Object copy2(o); // OK
}
fails to compile under clang3.5 with the error: (also fails under gcc)
error: excess elements in struct initializer
There are two different changes to Object that make this work. Either adding a data member to it, or giving it an empty copy constructor body
class Object {
private:
int i; // this fixes it
public:
Object() = default;
Object(const Object&) { } // and/or this fixes it as well
};
I don't see why these should make a difference.
This is a known bug and will hopefully be fixed in C++17 (not for C++14, see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1467). Your struct is an aggregate, so to initialize it with {someElement} there needs to be at least one data member, as you have discovered. Try providing an operator int(); and you will see it compiles.
Johannes's answer is useful, but let me elaborate on why it currently happens.
Both of the changes you describe affect your class by making it go from an aggregate to not an aggregate. See C++11 (N3485) § 8.5.1/1:
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal-initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
A constructor with a definition of = default is considered not user-defined.
Then, going down to list-initialization in § 8.5.4, we see:
List-initialization of an object or reference of type T is defined as follows:
If T is an aggregate, aggregate initialization is performed
And then a bunch of "Otherwise..." sections. Thus, changing either of these allows a constructor to be called instead of performing aggregate initialization.
The new proposed standardese for the list-initialization definition (as viewable in Johannes's link) provides a prioritized case of a single element in the list, and it having a type of (or really close to) the type of the object being initialized. Aggregate initialization would then be top priority after that.