i ran into a segfault running the following program
#include <iostream>
#include <vector>
template <typename Derived>
struct CRTPBase {
CRTPBase() {
func();
}
void func() {
static_cast<Derived*>(this)->_func();
}
};
struct CRTPChild : CRTPBase<CRTPChild>{
using CRTPBase<CRTPChild>::CRTPBase;
void _func(){
vec.resize(10);
vec[0] = 2;
}
std::vector<int> vec;
};
int main()
{
CRTPChild obj;
std::cout << obj.vec[0] << std::endl;
}
When i replace vec with a member of type int it doesn't segfault anymore. Why?
Your code has undefined behavior. The problem comes from the order of the initialization; for the derived class CRTPChild, the constructor of the base class CRTPBase<CRTPChild> is invoked firstly, after that the data member vec of CRTPChild get initialized. When _func is invoked (from the constructor of the base class) vec is not initialized at all.
2) Then, direct base classes are initialized in left-to-right order as
they appear in this class's base-specifier list
3) Then, non-static data members are initialized in order of
declaration in the class definition.
Changing the type to int it's still UB. UB means anything is possible, it might lead to segfault or might not.
When CRTPBase constructor is called, CRTPChild is not yet fully constructed, so calling it's member function is undefined behavior.
The way undefined behavior manifests itself depends on platform, compiler and phase of the moon.
In particular, when your member is an int, the fact that it is not yet constructed doesn't cause program to crash when you are using int - there are no invariants for int. Vector, on the other hand, has invariants, so accessing unconstructed vector will violate them, and cause incorrect memory access.
The base-class will be initialized (i.e. constructed) before the child class. That means when you call CRTPChild::_func the CRTPChild part of the object (including the vector) haven't been constructed yet. Using the vector in any way will lead to undefined behavior.
Don't access (non-static) members of child-classes in a base-class constructor.
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.
i ran into a segfault running the following program
#include <iostream>
#include <vector>
template <typename Derived>
struct CRTPBase {
CRTPBase() {
func();
}
void func() {
static_cast<Derived*>(this)->_func();
}
};
struct CRTPChild : CRTPBase<CRTPChild>{
using CRTPBase<CRTPChild>::CRTPBase;
void _func(){
vec.resize(10);
vec[0] = 2;
}
std::vector<int> vec;
};
int main()
{
CRTPChild obj;
std::cout << obj.vec[0] << std::endl;
}
When i replace vec with a member of type int it doesn't segfault anymore. Why?
Your code has undefined behavior. The problem comes from the order of the initialization; for the derived class CRTPChild, the constructor of the base class CRTPBase<CRTPChild> is invoked firstly, after that the data member vec of CRTPChild get initialized. When _func is invoked (from the constructor of the base class) vec is not initialized at all.
2) Then, direct base classes are initialized in left-to-right order as
they appear in this class's base-specifier list
3) Then, non-static data members are initialized in order of
declaration in the class definition.
Changing the type to int it's still UB. UB means anything is possible, it might lead to segfault or might not.
When CRTPBase constructor is called, CRTPChild is not yet fully constructed, so calling it's member function is undefined behavior.
The way undefined behavior manifests itself depends on platform, compiler and phase of the moon.
In particular, when your member is an int, the fact that it is not yet constructed doesn't cause program to crash when you are using int - there are no invariants for int. Vector, on the other hand, has invariants, so accessing unconstructed vector will violate them, and cause incorrect memory access.
The base-class will be initialized (i.e. constructed) before the child class. That means when you call CRTPChild::_func the CRTPChild part of the object (including the vector) haven't been constructed yet. Using the vector in any way will lead to undefined behavior.
Don't access (non-static) members of child-classes in a base-class constructor.
Consider this class:
#include <iostream>
struct foo {
int a = 42;
int b = bar();
int bar() { return a; }
};
int main(){
foo f;
std::cout << f.a << " " << f.b;
}
It prints the expected 42 42. Is it allowed by the standard to call a member function in a default member initializer?
The following I would expect to be undefined:
struct broken {
int a = bar();
int b = 42;
int bar() { return b; }
};
Unfortunately it does compile without warnings.
As you found, this is legal, but brittle and not recommended. When you specify default initializers for class members those are just syntactic sugar for use this value in the class member initializer list. So, if we look at when we can call a member function we find [class.cdtor]/1 and [class.cdtor]/4 which states:
1) For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior. For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
4) Member functions, including virtual functions ([class.virtual]), can be called during construction or destruction ([class.base.init]).[...]
emphasis mine
Since the constructor has begun executing, and we are allowed to call member functions, we are not in UB land.
The next thing we have to consider is construction order, since the members depend on that. That information is in [class.base.init]/13
Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
So, the members are constructed in the order they are declared in the class which means in your first example you refer to a after it has been initialized so you are not in UB land.
In your second example you are referring to an object that has not yet been initialized and reading an uninitialized object's value is undefined behavior.
The standard says here:
Member functions (including virtual member functions, [class.virtual]) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator ([expr.typeid]) or of a dynamic_cast ([expr.dynamic.cast]). However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the program has undefined behavior.
Since you have no base classes, the member functions can be called for broken even if it is under construction. Your a will be initialized with indeterminate value.
I was a bit premature. As seen in the other answer, there is the problem, that the function reads from an unitialized value which is undefined behavior. So not the call of this function in itself, but rather what it does is UB.
#include <cstdio>
class A
{
public:
A(int){puts("3");};
int foo(){puts("4");return 10;}
};
int main()
{
A a(a.foo());
return 0;
}
Outputs 4 and 3.
It calls a member function before calling the constructor. Is the behavior defined by the standard?
§12.7 [class.cdtor]/p1:
For an object with a non-trivial constructor, referring to any
non-static member or base class of the object before the constructor
begins execution results in undefined behavior.
A conforming compiler is allowed to emit code that blows your legs off.
Yes. In practice, it may work, because A::foo doesn't se any state from a instance. You should never write code like this (and you should probably correct it).
Can I pass "this" to a function as a pointer, from within the class constructor, and use it to point at the object's members before the constructor returns?
Is it safe to do this, so long as the accessed members are properly initialized before the function call?
As an example:
#include <iostream>
class Stuff
{
public:
static void print_number(void *param)
{
std::cout << reinterpret_cast<Stuff*>(param)->number;
}
int number;
Stuff(int number_)
: number(number_)
{
print_number(this);
}
};
void main() {
Stuff stuff(12345);
}
I thought this wouldn't work, but it seems to. Is this standard behavior, or just undefined behavior going my way?
When you instantiate an object in C++, the code in the constructor is the last thing executed. All other initialization, including superclass initialization, superclass constructor execution, and memory allocation happens beforehand. The code in the constructor is really just to perform additional initialization once the object is constructed. So it is perfectly valid to use a "this" pointer in a class' constructor and assume that it points to a completely constructed object.
Of course, you still need to beware of uninitialized member variables, if you haven't already initialized them in your constructor code.
You can find a good answer to this here (C++ FAQ).
All inherited members and members of the calling class are guaranteed to have been constructed at the start of the constructor's code execution and so can be referenced safely within it.
The main gotcha is that you should not call virtual functions on this. Most times I've tried this it just ends up calling the base class's function, but I believe the standard says the result is undefined.
As a side-note on the presented code, I would instead templatize the void*:
class Stuff
{
public:
template <typename T>
static void print_number(const T& t)
{
std::cout << t.number;
}
int number;
Stuff(int number_)
: number(number_)
{
print_number(*this);
}
};
Then you'd get a compile error if the type of t doesn't have a number member.
Andy, I think you're wrong about the undefined part of the standard.
When you're in the constructor, "this" is a pointer to an object whose type is the base class of the object you're creating, which means that virtual functions partially implemented in the base class will be called and the pointers in the virtual table won't be followed.
More info in the C++ Faq Lite...