why in this case copy constructor is matched? - c++11

I am curious about the C++11 list-initialization. I defined a class:
class base{
base() { cout << "default ctor" << endl;}
base(std::initiazer_list<base> il) { cout << "list initialization << endl;}
base(const base &rhs) { cout << "copy ctor" << endl;}
}
In main function, I initialize two objects to test my class.
int main()
{
base obj{}; // default ctor
base obj2{obj}; // copy ctor
}
I learn from 《effective modern C++》 that calls using list-initialization syntax strongly prefer the overloads taking std::initializer_list.
So, in my case, I think the second ctor will be called, however, the third is called.
Can you explain why?

Demo
default ctor
copy ctor
list initialization
So, it's correct in the sense that first the copy constructor is called to copy obj to std::initializer_list<base> then the appropriate constructor overload (which is the one for initializer list) is called.
base obj2{obj}; // copy ctor
As you can see this consists of two calls, not one. One to copy, one to list initialize. List initializer is the strongly preferred overload. The copy is to initialize the argument to that overload.
Edit: gcc and clang vary on this irrespective of version.
GCC Head
Clang Head
I think what's happening is that libstdc++ implementation of std::initializer_list creates temporary object, whereas clang picks up the copy constructor instead of initializer list overload.
Edit2:
I have checked this on my local system, clang with -stdlib=libstdc++ does create temporary for std::initializer_list hence a call to copy constructor exists.
All compilers are correct that

Related

c++11 local static member variable destruction order for shared_ptr objects

I've been struggling with a destructor call order which I cannot really understand.
Say we have the following definitions:
#include <memory>
#include <iostream>
class DummyClass {
std::string name;
public:
DummyClass(std::string name) : name(name) { std::cout << "DummyClass(" << name << ")" << std::endl; }
~DummyClass() { std::cout << "~DummyClass(" << name << ")" << std::endl; }
};
class TestClass {
private:
static DummyClass dummy;
static DummyClass& objects() {
static DummyClass dummy("inner");
return dummy;
}
public:
TestClass() {
std::cout << "TestClass" << std::endl;
std::cout << "TestClass Objects is: " << &objects() << std::endl;
}
virtual ~TestClass() {
std::cout << "~TestClass Objects is: " << &objects() << std::endl;
std::cout << "~TestClass" << std::endl;
}
};
DummyClass TestClass::dummy("outer");
Now, If I instantiate the TestClass as follows:
TestClass *mTest = nullptr;
int main() {
mTest = new TestClass(); delete mTest;
return 0;
}
The output obtained is the one I would expect:
DummyClass(outer)
TestClass
DummyClass(inner)
TestClass Objects is: 0x....
~TestClass Objects is: 0x....
~TestClass
~DummyClass(inner)
~DummyClass(outer)
But, now, if I use a shared_ptr for mTest, like:
std::shared_ptr<TestClass> mTest;
int main() {
mTest = std::make_shared<TestClass>();
return 0;
}
the output produced is:
DummyClass(outer)
TestClass
DummyClass(inner)
TestClass Objects is: 0x....
~DummyClass(inner)
~TestClass Objects is: 0x....
~TestClass
~DummyClass(outer)
Can someone explain why is the DummyClass inner object being destroyed before the end of the TestClass object destructor, in this particular case?
I found consistent behavior for gcc 5.2.0 using -std=gnu++11 and clang 3.8.0 with -std=c++11 but could not find any particular documentation citing this example.
Edit: To clarify: all of the code above was written in the same translation unit (*.cpp file) in the presented order. It is a simplification of a usage case where I have a header only class definition which must hold a static list of this pointers to derived class objects. These pointers are added via ctor and removed when the dtor is reached. The problem is triggered when destroying the last object. The list is kept inside a static method and accessed through it to achieve the header only goal.
The rules for all objects with static storage duration (namespace members, static class members, and static objects in function definitions) are:
If the entire initialization can be considered a constant expression, that initialization happens before anything else. (Doesn't apply to anything in your examples.) Otherwise,
Namespace members and static class members are guaranteed to begin initialization at some point before any function in the same translation unit is called. (In most implementations, if we ignore dynamic library loading, all of these happen before main begins. In your examples, since main is in the same TU, we know they happen before main.)
Namespace members and static class members defined in the same TU begin their initializations in the order of their definitions.
For namespace members and static class members defined in different TUs, there is no guarantee on order of initialization!
Static objects defined inside a function begin their initialization the first time program control reaches the definition (if ever).
When main returns or std::exit is called, all objects with static storage duration are destroyed in order opposite to when each completed its initialization.
So in your second example:
Initialization of TestClass::dummy begins. First a temporary std::string is created, and then DummyClass::DummyClass(std::string) is called.
The DummyClass constructor does a std::string copy, then outputs "DummyClass(outer)\n". The temporary std::string is destroyed. Initialization of TestClass::dummy is complete.
Initialization of ::mTest begins. This calls std::shared_ptr<TestClass>::shared_ptr().
The shared_ptr constructor sets up the smart pointer to be null. Initialization of ::mTest is complete.
main begins.
The std::make_shared call ends up creating a TestClass object, calling TestClass::TestClass(). This constructor first prints "TestClass\n", then calls TestClass::objects().
Inside TestClass::objects(), initialization of local object dummy begins. Again a temporary std::string is created, and DummyClass::DummyClass(std::string) is called.
The DummyClass constructor does a std::string copy, then outputs "DummyClass(inner)\n". The temporary std::string is destroyed. Initialization of objects' dummy is complete.
TestClass::TestClass() continues, printing "TestClass Objects is: 0x...\n". Initialization of the dynamic TestClass object is complete.
Back in main, the make_shared function returns a temporary std::shared_ptr<TestClass>. A move assignment moves from the returned temporary to ::mTest, then the temporary is destroyed. Note that although the TestClass object is associated with ::mTest, it has dynamic storage duration, not static storage duration, so the above rules do not apply to it.
main returns. C++ begins destroying objects with static storage duration.
The last static object to finish initialization was the dummy local of TestClass::objects() at step 8 above, so it is destroyed first. Its destructor body outputs "~DummyClass(inner)\n".
The next object to finish initializing was ::mTest in step 4 above, so its destruction begins next. The ~shared_ptr destructor ends up destroying the owned dynamic TestClass object.
The TestClass::~TestClass() destructor body first calls TestClass::objects().
In TestClass::objects(), we encounter the definition of an already destroyed function-local static, which is Undefined Behavior! Apparently though, your implementation does nothing but return a reference to the storage that formerly contained dummy, and it's probably a good thing you didn't do anything with it other than take the address.
TestClass::~TestClass() continues, outputting "~TestClass Objects is: 0x...\n" and then "~TestClass\n".
The ~shared_ptr destructor for ::mTest deallocates associated memory and completes.
Finally, the first static object to finish initialization was TestClass::dummy, in step 2 above, so it is destroyed last. The DummyClass::~DummyClass destructor body outputs "~DummyClass\n". The program is finished.
So the big difference between your two examples is the fact that the TestClass destruction gets delayed until the shared_ptr is destroyed - it doesn't really matter when in the scheme of things the TestClass was created. Since the shared_ptr was created before the "inner" DummyClass in the second example, its destruction happens after the "inner" object is gone, causing that Undefined Behavior.
If this is a simplification of an actual issue you ran into and need to fix, you might try adding something like
class TestClass {
// ...
public:
class ForceInit {
ForceInit() { TestClass::objects(); }
};
// ...
};
// ...
TestClass::ForceInit force_init_before_mTest;
std::shared_ptr<TestClass> mTest;
It's not related to shared_ptr but to the order of destruction of global variables in modules (cc files). The spec states that the order is undefined so you can not assume that static inner object will be destroyed after or before another global object. If you need to have a consistent order of destruction I advise that you take care of it explicitly.

move constructor called on return instead of copy

Today I found that this code doesn't work as I expect it to work.
According to my knowledg for L-values copy constructor should be called while for R-values it should choose move constructor. Otherwise what's the purpose of std::move which actually does nothing but casts to R-value. I was expecting that return obj will call copy constructor, but it calls move.
I understand that copy is useless here, but that's about rules. What if my copy constructor has a side effect and that's my case (I know that it shouldn't, but technically it can - for example here: std::cout call).
Is there anything in standard that allows such behavior? Also how can I force a copy?
#include <iostream>
class X
{
public:
X() = default;
X(const X& r): i(r.i)
{
std::cout << "copy ctor" << std::endl;
}
X(const X&& r): i(r.i)
{
std::cout << "move ctor" << std::endl;
}
int i = 0;
};
X foo()
{
X obj;
obj.i = 10;
return obj;
}
int main()
{
X x = foo();
}
move ctor
move ctor
From cppreference (emphasis mine):
If [the returned expression] is an lvalue expression and the conditions for copy elision are met, or would be met, except that [the returned expression] names a function parameter, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if [the returned expression] were an rvalue expression (thus it may select the move constructor or a copy constructor taking reference to const), and if no suitable conversion is available, overload resolution is performed the second time, with lvalue [returned expression] (so it may select the copy constructor taking a reference to non-const).
The above rule applies even if the function return type is different from the type of [the returned expression] (copy elision requires same type)
Long story short, return implicitly tries to move what you return when it makes sense. It will only copy as a last resort (no move constructor available, for example).

C++11 implicit copy constructor while implementing explicitly a constructor

I ran into a problem. I implemented a constructor for a class, but why are implicitly generated the other constructors, like the copy one?
I thought, that if I define a constructor explicitly, then the compiler doesn't generates implicitly other ones. I'm really hoping, that this a VC++ specific thing, and that this code doesn't conforms to ISO:IEC C++11:
class Foo
{
int bar;
public:
Foo(int&& arg) : bar(arg) { cout << "RConstruction" << endl; }
};
int main(int, const char*[])
{
Foo f = Foo(42);
/* Create unused temporary on the stack */
Foo::Foo(f); /* calling Foo::Foo(const Foo&): this shouldn't work... */
return (0);
}
Please keep in mind, that this is a sample code, created exactly for this situation, for demonstration purposes, I expect answers only that strictly relate to this question.
That's not a move constructor, so it doesn't suppress any implicit ones.
Just like Foo(const int&) isn't a copy constructor, Foo(int&&) isn't a move constructor, it's just a constructor taking an rvalue reference.
A move constructor looks like one of:
Foo(Foo&&)
Foo(const Foo&&)
Foo(volatile Foo&&)
Foo(const volatile Foo&&)
I thought, that if I define a constructor explicitly, then the compiler doesn't generates implicitly other ones.
If you define any constructor the compiler doesn't generate the default constructor, but it still generates the others. Define the as deleted if you don't want them:
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
You did not declare a move constructor, but a regular constructor : no implicit constructor will be deleted.
A move constructor would be of the form Foo(Foo&& arg) (with any cv-qualifier on the argument)
Also note that this statement is not valid C++ :
Foo::Foo(f);
Maybe you meant :
Foo g = Foo(f);

Is my compiler optimizing away the rvalue returned from function? [duplicate]

What is copy elision? What is (named) return value optimization? What do they imply?
In what situations can they occur? What are limitations?
If you were referenced to this question, you're probably looking for the introduction.
For a technical overview, see the standard reference.
See common cases here.
Introduction
For a technical overview - skip to this answer.
For common cases where copy elision occurs - skip to this answer.
Copy elision is an optimization implemented by most compilers to prevent extra (potentially expensive) copies in certain situations. It makes returning by value or pass-by-value feasible in practice (restrictions apply).
It's the only form of optimization that elides (ha!) the as-if rule - copy elision can be applied even if copying/moving the object has side-effects.
The following example taken from Wikipedia:
struct C {
C() {}
C(const C&) { std::cout << "A copy was made.\n"; }
};
C f() {
return C();
}
int main() {
std::cout << "Hello World!\n";
C obj = f();
}
Depending on the compiler & settings, the following outputs are all valid:
Hello World!
A copy was made.
A copy was made.
Hello World!
A copy was made.
Hello World!
This also means fewer objects can be created, so you also can't rely on a specific number of destructors being called. You shouldn't have critical logic inside copy/move-constructors or destructors, as you can't rely on them being called.
If a call to a copy or move constructor is elided, that constructor must still exist and must be accessible. This ensures that copy elision does not allow copying objects which are not normally copyable, e.g. because they have a private or deleted copy/move constructor.
C++17: As of C++17, Copy Elision is guaranteed when an object is returned directly:
struct C {
C() {}
C(const C&) { std::cout << "A copy was made.\n"; }
};
C f() {
return C(); //Definitely performs copy elision
}
C g() {
C c;
return c; //Maybe performs copy elision
}
int main() {
std::cout << "Hello World!\n";
C obj = f(); //Copy constructor isn't called
}
Common forms of copy elision
For a technical overview - skip to this answer.
For a less technical view & introduction - skip to this answer.
(Named) Return value optimization is a common form of copy elision. It refers to the situation where an object returned by value from a method has its copy elided. The example set forth in the standard illustrates named return value optimization, since the object is named.
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
Regular return value optimization occurs when a temporary is returned:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
return Thing();
}
Thing t2 = f();
Other common places where copy elision takes place is when an object is constructed from a temporary:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
void foo(Thing t);
Thing t2 = Thing();
Thing t3 = Thing(Thing()); // two rounds of elision
foo(Thing()); // parameter constructed from temporary
or when an exception is thrown and caught by value:
struct Thing{
Thing();
Thing(const Thing&);
};
void foo() {
Thing c;
throw c;
}
int main() {
try {
foo();
}
catch(Thing c) {
}
}
Common limitations of copy elision are:
multiple return points
conditional initialization
Most commercial-grade compilers support copy elision & (N)RVO (depending on optimization settings). C++17 makes many of the above classes of copy elision mandatory.
Standard reference
For a less technical view & introduction - skip to this answer.
For common cases where copy elision occurs - skip to this answer.
Copy elision is defined in the standard in:
12.8 Copying and moving class objects [class.copy]
as
31) When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class
object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases,
the implementation treats the source and target of the omitted copy/move operation as simply two different
ways of referring to the same object, and the destruction of that object occurs at the later of the times
when the two objects would have been destroyed without the optimization.123 This elision of copy/move
operations, called copy elision, is permitted in the following circumstances (which may be combined to
eliminate multiple copies):
— in a return statement in a function with a class return type, when the expression is the name of a
non-volatile automatic object (other than a function or catch-clause parameter) with the same cvunqualified
type as the function return type, the copy/move operation can be omitted by constructing
the automatic object directly into the function’s return value
— in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a
function or catch-clause parameter) whose scope does not extend beyond the end of the innermost
enclosing try-block (if there is one), the copy/move operation from the operand to the exception
object (15.1) can be omitted by constructing the automatic object directly into the exception object
— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved
to a class object with the same cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the omitted copy/move
— when the exception-declaration of an exception handler (Clause 15) declares an object of the same type
(except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted
by treating the exception-declaration as an alias for the exception object if the meaning of the program
will be unchanged except for the execution of constructors and destructors for the object declared by
the exception-declaration.
123) Because only one object is destroyed instead of two, and one copy/move constructor is not executed, there is still one
object destroyed for each one constructed.
The example given is:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
and explained:
Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing:
the copying of the local automatic object t into the temporary object for the return value of function f()
and the copying of that temporary object into object t2. Effectively, the construction of the local object t
can be viewed as directly initializing the global object t2, and that object’s destruction will occur at program
exit. Adding a move constructor to Thing has the same effect, but it is the move construction from the
temporary object to t2 that is elided.
Copy elision is a compiler optimization technique that eliminates unnecessary copying/moving of objects.
In the following circumstances, a compiler is allowed to omit copy/move operations and hence not to call the associated constructor:
NRVO (Named Return Value Optimization): If a function returns a class type by value and the return statement's expression is the name of a non-volatile object with automatic storage duration (which isn't a function parameter), then the copy/move that would be performed by a non-optimising compiler can be omitted. If so, the returned value is constructed directly in the storage to which the function's return value would otherwise be moved or copied.
RVO (Return Value Optimization): If the function returns a nameless temporary object that would be moved or copied into the destination by a naive compiler, the copy or move can be omitted as per 1.
#include <iostream>
using namespace std;
class ABC
{
public:
const char *a;
ABC()
{ cout<<"Constructor"<<endl; }
ABC(const char *ptr)
{ cout<<"Constructor"<<endl; }
ABC(ABC &obj)
{ cout<<"copy constructor"<<endl;}
ABC(ABC&& obj)
{ cout<<"Move constructor"<<endl; }
~ABC()
{ cout<<"Destructor"<<endl; }
};
ABC fun123()
{ ABC obj; return obj; }
ABC xyz123()
{ return ABC(); }
int main()
{
ABC abc;
ABC obj1(fun123()); //NRVO
ABC obj2(xyz123()); //RVO, not NRVO
ABC xyz = "Stack Overflow";//RVO
return 0;
}
**Output without -fno-elide-constructors**
root#ajay-PC:/home/ajay/c++# ./a.out
Constructor
Constructor
Constructor
Constructor
Destructor
Destructor
Destructor
Destructor
**Output with -fno-elide-constructors**
root#ajay-PC:/home/ajay/c++# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors
root#ajay-PC:/home/ajay/c++# ./a.out
Constructor
Constructor
Move constructor
Destructor
Move constructor
Destructor
Constructor
Move constructor
Destructor
Move constructor
Destructor
Constructor
Move constructor
Destructor
Destructor
Destructor
Destructor
Destructor
Even when copy elision takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
You should permit such copy elision only in places where it won’t affect the observable behavior of your software. Copy elision is the only form of optimization permitted to have (i.e. elide) observable side-effects. Example:
#include <iostream>
int n = 0;
class ABC
{ public:
ABC(int) {}
ABC(const ABC& a) { ++n; } // the copy constructor has a visible side effect
}; // it modifies an object with static storage duration
int main()
{
ABC c1(21); // direct-initialization, calls C::C(42)
ABC c2 = ABC(21); // copy-initialization, calls C::C( C(42) )
std::cout << n << std::endl; // prints 0 if the copy was elided, 1 otherwise
return 0;
}
Output without -fno-elide-constructors
root#ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp
root#ajay-PC:/home/ayadav# ./a.out
0
Output with -fno-elide-constructors
root#ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors
root#ajay-PC:/home/ayadav# ./a.out
1
GCC provides the -fno-elide-constructors option to disable copy elision.
If you want to avoid possible copy elision, use -fno-elide-constructors.
Now almost all compilers provide copy elision when optimisation is enabled (and if no other option is set to disable it).
Conclusion
With each copy elision, one construction and one matching destruction of the copy are omitted, thus saving CPU time, and one object is not created, thus saving space on the stack frame.
Here I give another example of copy elision that I apparently encountered today.
# include <iostream>
class Obj {
public:
int var1;
Obj(){
std::cout<<"In Obj()"<<"\n";
var1 =2;
};
Obj(const Obj & org){
std::cout<<"In Obj(const Obj & org)"<<"\n";
var1=org.var1+1;
};
};
int main(){
{
/*const*/ Obj Obj_instance1; //const doesn't change anything
Obj Obj_instance2;
std::cout<<"assignment:"<<"\n";
Obj_instance2=Obj(Obj(Obj(Obj(Obj_instance1)))) ;
// in fact expected: 6, but got 3, because of 'copy elision'
std::cout<<"Obj_instance2.var1:"<<Obj_instance2.var1<<"\n";
}
}
With the result:
In Obj()
In Obj()
assignment:
In Obj(const Obj & org)
Obj_instance2.var1:3

allocating memory for derived class members based on boost smart pointers in the base class through CRTP

This part of the question provides background information and can be ignored
I am working on a template library which heavily relies on the use of the curiously recurring template pattern. The idea behind the class structure is that the user can either
1). Use predefined classes with standard methods. These classes are very simple leafs of the base class that only provide constructors/destructor, declare the variable members and declare the base class(es) as friend(s). All methods that operate on the variable members of the derived classes are defined in the base class(es).
2). Use the base classes to create his/her own extensions. This method also allows users to introduce their own methods that operate on the same variable members.
Only a single level inheritance is enforced by design.
My question is, primarily, about the clause 2. In the current implementation the user has to define all constructors implicitly (i.e. describe full process of memory allocation for dynamic variable members of the class, etc).
Question
The example below demonstrates an investigation into a possibility to use the CRTP to provide the definition of the allocation of the memory of the heap variables of the derived classes in the base class constructors.
Part of the base class
template<class TLeafType, class MyClass> class sysBaseDiscreteTrajectoryPoint {
...
//one of the base constructors
sysBaseDiscreteTrajectoryPoint(const MyClass& MyClassInstance) {
std::cout << "Base additional constructor called" << std::endl;
std::cout << asLeaf().Point << std::endl;
asLeaf().Point=new MyClass(MyClassInstance);
std::cout << asLeaf().Point << std::endl;
}
TLeafType& asLeaf(void) {
return static_cast<TLeafType&>(*this);
}
...
};
The derived class:
template<class MyClass>
class sysDiscreteTrajectoryPoint: public sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass> {
...
friend class sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>;
private:
MyClass* Point;
public:
sysDiscreteTrajectoryPoint(const MyClass& MyClassInstance): sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>(MyClassInstance){
std::cout << "Derived additional constructor called " << std::endl;
std::cout << Point << std::endl;
std::cout << *Point << std::endl;
}
...
}
main:
int a(5);
sysDiscreteTrajectoryPoint<int> A(a);
The code produces the following output:
Base additional constructor called
0x847ff4
0x8737008
Derived additional constructor called
0x8737008
5
Derived destructor called
Base destructor called
The output suggests that the concept may be feasible. However, I have two questions.
1). I would like to ensure that I understand all processes that happen during the execution of the code. In particular, I am interested in the efficiency of the process, as I may need to instantiate a substantial amount of objects from the classes presented above and I would like to understand what happens with Point (are there any hidden redefinitions?)
2). The question is related to the use of the library boost for the definition of smart pointers for the members of the derived class. When I tried replacing the raw pointer with boost::shared_ptr, I received a segmentation fault error when trying to allocate memory for the member of the derived class through the base class. The important sections of the code are shown below.
Part of the base class:
template<class TLeafType, class MyClass> class sysBaseDiscreteTrajectoryPoint {
...
//one of the base constructors
sysBaseDiscreteTrajectoryPoint(const MyClass& MyClassInstance) {
std::cout << "Base additional constructor called" << std::endl;
std::cout << asLeaf().Point << std::endl;
asLeaf().Point.reset(new MyClass(MyClassInstance));
std::cout << asLeaf().Point << std::endl;
}
TLeafType& asLeaf(void) {
return static_cast<TLeafType&>(*this);
}
...
};
Part of the derived class:
template<class MyClass>
class sysDiscreteTrajectoryPoint: public sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass> {
...
friend class sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>;
private:
boost::shared_ptr<MyClass> Point;
public:
sysDiscreteTrajectoryPoint(const MyClass& MyClassInstance): sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>(MyClassInstance){
std::cout << "Derived additional constructor called " << std::endl;
std::cout << Point << std::endl;
std::cout << *Point << std::endl;
}
...
}
main:
int a(5);
sysDiscreteTrajectoryPoint<int> A(a);
The code produces the following output:
Base additional constructor called
0x28d324
Segmentation fault
I have also tried scoped_ptr. However, it failed at run time but with a different error:
Base additional constructor called
*** glibc detected *** ./TestSystem: free(): invalid pointer: 0x00d3fff4 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x6b961)[0xc4e961]
...
I assume that it is related to the specifics of the operation of boost smart pointers. Does anyone know how to resolve this issue?
The address of the shared_ptr is known at compile time for the reasons given in the above answers, but the shared_ptr itself is still uninitialised because the derived-class constructor has not yet been called, and thus has not had a chance to implicitly call its instance members' constructors, including the default constructor for shared_ptr. Therefore, when you call reset() to assign the shared_ptr, it first tries to release (and possibly delete) the object at whatever spurious address it contains (to avoid leaking an existing referent) before assigning and referencing the new object. That first step, I believe, is what causes the segfault.
If the shared_ptr constructor ran first, it would null its contained raw pointer, preventing the subsequent reset() call from trying to release an object at the spurious address.
Using asLeaf() to access the derived class from the base-class constructor is inherently unsafe for non-POD types because construction is incomplete (the derived class's members are not yet constructed). This is, incidentally, why virtual method calls from a base constructor will never call overrides from more-derived classes - the language explicitly prevents overrides from being called until construction of the whole object is complete because in most cases the state of the whole object is not yet defined.
There may be better solutions for you, but one approach that would work would be to remove that initialization code from the base class's constructor and put it in an init() function that is called explicitly at every instantiation of the derived class. init() can still live in the base class, but it's safer because everything will have been initialized by the time it runs.
Side note: avoid putting small objects in shared_ptr without good reason. You might have a legitimate need for it in this case, but in general I prefer direct aggregation of members to single-owner pointers and single-owner pointers to shared pointers wherever possible because the overhead escalates. Single-owner pointers involve heap allocations, and shared pointers also add to this the cost of counting/tracking owners so that the object can be deleted when unreachable.
How is it possible that you can access Point member belonging to the derived class from base constructor? When the base constructor is being invoked, the derived class part does not exist. Perhaps it works just "by accident".
But it certainly fails with shared_ptr, because you attempt to assign it before it has a chance to get initialized.

Resources