Is this an old C++ style constructor? - c++

Here a piece of C++ code.
In this example, many code blocks look like constructor calls.
Unfortunately, block code #3 is not (You can check it using https://godbolt.org/z/q3rsxn and https://cppinsights.io).
I think, it is an old C++ notation and it could explain the introduction of the new C++11 construction notation using {} (cf #4).
Do you have an explanation for T(i) meaning, so close to a constructor notation, but definitely so different?
struct T {
T() { }
T(int i) { }
};
int main() {
int i = 42;
{ // #1
T t(i); // new T named t using int ctor
}
{ // #2
T t = T(i); // new T named t using int ctor
}
{ // #3
T(i); // new T named i using default ctor
}
{ // #4
T{i}; // new T using int ctor (unnamed result)
}
{ // #5
T(2); // new T using int ctor (unnamed result)
}
}
NB: thus, T(i) (#3) is equivalent to T i = T();

The statement:
T(i);
is equivalent to:
T i;
In other words, it declares a variable named i with type T. This is because parentheses are allowed in declarations in some places (in order to change the binding of declarators) and since this statement can be parsed as a declaration, it is a declaration (even though it might make more sense as an expression).

You can use Compiler Explorer to see what happens in assembler.
You can see that #1,#2 #4 and #5 do same thing but strangly #3 call the other constructor (the base object constructor).
Does anyone have an explanation?
Assembler code :
::T() [base object constructor]:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
nop
pop rbp
ret
T::T(int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 42
// #1
mov edx, DWORD PTR [rbp-4]
lea rax, [rbp-7]
mov esi, edx
mov rdi, rax
call T::T(int)
// #2
mov edx, DWORD PTR [rbp-4]
lea rax, [rbp-8]
mov esi, edx
mov rdi, rax
call T::T(int)
// #3
lea rax, [rbp-9]
mov rdi, rax
call T::T() [complete object constructor]
// #4
mov edx, DWORD PTR [rbp-4]
lea rax, [rbp-6]
mov esi, edx
mov rdi, rax
call T::T(int)
// #5
lea rax, [rbp-5]
mov esi, 2
mov rdi, rax
call T::T(int)
mov eax, 0
leave
ret

Related

C++ compiler: inline usage of a non-inline function defined in the same module

T.hpp
class T
{
int _i;
public:
int get() const;
int some_fun();
};
T.cpp
#include "T.hpp"
int T::get() const
{ return _i; }
int T::some_fun()
{
// noise
int i = get(); // (1)
// noise
}
get() is a non-inline function, however, it's defined in the same module as some_fun. Since the compiler can see the definition of get in the context of some_fun, do compilers, in optimized builds at least, apply the optimization of replacing get() by just _i in line (1)?
If I'm not wrong, I think that, with the exception of templates, the compiler only does a one-pass parsing. What if get is defined after some_fun?
Ok, I answered myself. I thought I didn't speak assembly but it wasn't that hard to try.
Code:
class T
{
int _i = 5;
public:
int get() const;
int some_fun();
};
int T::get() const { return _i; }
int T::some_fun()
{
int i = get();
return i;
}
int main()
{
T o;
return o.some_fun();
}
Non-optimized assembly output (using godbolt.org). A lot of stuff but you can see the explicit calls:
T::get() const:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov rax, QWORD PTR [rbp-8]
mov eax, DWORD PTR [rax]
pop rbp
ret
T::some_fun():
push rbp
mov rbp, rsp
sub rsp, 24
mov QWORD PTR [rbp-24], rdi
mov rax, QWORD PTR [rbp-24]
mov rdi, rax
call T::get() const // !!!!
mov DWORD PTR [rbp-4], eax
mov eax, DWORD PTR [rbp-4]
leave
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 5
lea rax, [rbp-4]
mov rdi, rax
call T::some_fun() // !!!!
nop
leave
ret
Optimized output (-O3):
T::get() const:
mov eax, DWORD PTR [rdi]
ret
T::some_fun():
mov eax, DWORD PTR [rdi]
ret
main:
mov eax, 5
ret
Here, some_fun has inlined the call to get (the call instruction has been removed and its definition is the same as get now), but the get function is still defined.
main went even further by doing an inline substitution of the call to some_fun and then realizing that o hasn't changed and at that point it still retains its default value of 5, so main directly returns 5 without even creating o.

Why can I create an object when there is no explicitly defined constructor?

I have two pieces of code:
Piece 1:
class Test{
public:
Test(int a) {};
};
Piece 2:
class Test{
public:
Test() {};
};
When i want to create an array of some size with the Test object i have a problem. With the first piece of code i can't create the array as Test arr[some number], instead i must to do Test *arr[some number] = new Test();
But with the second piece of code i can do Test arr[some number].
Apparently, without arguments in the constructor, I can create the array without having to point to the object's memory, but if I use constructor with args, I have to point to the object's memory address and then use the arrow operator to access its methods.
Why is this happening?
From logical point of view, which permeates C++ language rules:
Constructors of class describes ways one object of class-type can be created.
If any user-defined constructor was provided, compiler would not generate default constructor. If no constructors defined, compiler would generate copy, move and default constructors.
Results in:
If there is no default constructor, object cannot be created by default initialization. Because there is no constructor for that.
To allow object be created that way, user should specify constructor for default initialization.
class Test{
public:
Test() = default;
Test(int a) {};
};
Note that array is another object, consisting of objects that are its elements. It still can be value-initialized, e.g.
Test a1[3] = {3,5,7}; // those values are passed to constructors of individual elements
// or
Test a2[3] = {{3},{5},{7}};
But if Test doesn't have a default constructor, i.e. it cannot be default initialized, code would be ill-formed if count of elements in list would be smaller than size of array.
Test a[13] = {3,5,7}; // for elements 3-12 Test() will be called
Here's a test example from Compiler Explorer
Here's the actual code:
class Test1 {
public:
Test1() {}
};
class Test2 {
public:
Test2(int a) {}
};
int main() {
// Compiles just fine
Test1 test1Arr[5];
// Fails to Compile
Test2 test2Arr[5];
return 0;
}
Now, this fails to compile because of the 2nd case... If we comment Test2 test2Arr[5]; so that it compiles it will generate this assembly for gcc 10.2...
Test1::Test1() [base object constructor]:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
push r12
push rbx
sub rsp, 16
lea rax, [rbp-21]
mov ebx, 4
mov r12, rax
.L4:
test rbx, rbx
js .L3
mov rdi, r12
call Test1::Test1() [complete object constructor]
add r12, 1
sub rbx, 1
jmp .L4
.L3:
mov eax, 0
add rsp, 16
pop rbx
pop r12
pop rbp
ret
So as you can see you can create an array of class or struct objects with default constructors easily.
The reason it is failing in the other case is that it is not a default constructor. You have an explicit argument making it a user-defined constructor and you have not declared a default constructor.
In other words, if you were to create a single instance of this class object you must pass an argument to its constructor.
Test2 test2(3);
Now since you want to create an array of these and without using dynamic memory, you can take advantage of brace-initialization
If you were to do this:
Test2 testArra[5] = {0,1,2,3,4};
This will compile and this will generate the following assembly:
Test1::Test1() [base object constructor]:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
nop
pop rbp
ret
Test2::Test2(int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
push r12
push rbx
sub rsp, 16
lea rax, [rbp-21]
mov ebx, 4
mov r12, rax
.L5:
test rbx, rbx
js .L4
mov rdi, r12
call Test1::Test1() [complete object constructor]
add r12, 1
sub rbx, 1
jmp .L5
.L4:
lea rbx, [rbp-26]
mov esi, 0
mov rdi, rbx
call Test2::Test2(int)
add rbx, 1
mov esi, 1
mov rdi, rbx
call Test2::Test2(int)
add rbx, 1
mov esi, 2
mov rdi, rbx
call Test2::Test2(int)
add rbx, 1
mov esi, 3
mov rdi, rbx
call Test2::Test2(int)
lea rax, [rbx+1]
mov esi, 4
mov rdi, rax
call Test2::Test2(int)
mov eax, 0
add rsp, 16
pop rbx
pop r12
pop rbp
ret
This works because it will assign each value of the brace-initialization through the use of the comma-operator to each element of the array's user-defined constructor...
Now, if you define both constructors within the same class as such:
class Test3 {
public:
Test3() {}
Test3(int a) {}
};
int main() {
Test3 test3Arr[5];
return 0;
}
This will compile generating this assembly code:
Test3::Test3() [base object constructor]:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
push r12
push rbx
sub rsp, 16
lea rax, [rbp-21]
mov ebx, 4
mov r12, rax
.L4:
test rbx, rbx
js .L3
mov rdi, r12
call Test3::Test3() [complete object constructor]
add r12, 1
sub rbx, 1
jmp .L4
.L3:
mov eax, 0
add rsp, 16
pop rbx
pop r12
pop rbp
ret
which can be seen here.
The bottom line is... when you declare-define a user-defined constructor, the compiler will not automatically generate a default constructor for you and if you want that behavior you must define it yourself. Now, if you do not have any user-defined constructors and everything is default initialized, then you don't have to define any constructor and the compiler will automatically generate the default constructor for you.

Why can't the constructor return void expression? [duplicate]

This question already has answers here:
Why do constructors not return values?
(19 answers)
Closed 2 years ago.
The following example does not meet the standard:
void f();
struct A {
A() {
return f(); // should be replaced to `f(); return;`
}
};
But when the constructor is replaced with a function that returns void, this is legal.
I know this is required by the standard, as follows:
12.1 Constructors
12 No return type (not even void) shall be specified for a constructor. A return statement in the body of a constructor shall not specify a return value.
But why?
A constructor is a special method that is designed to initialize a new instance of that class. Under the hood they are not actually called (see this question), so really any return would be inaccurate since there really nothing is returned. Nothing here is different than void, because void is a type, and returning a type in a block of code which does not return would be confusing and misleading syntax.
Further, constructors are called as part of initialization and only writes the values of the arguments to sections of memory in the same way writing int n = 5, writes the value 5 to a block of memory which is referenced when n is used.
To the user the initialization process seems like it is just a function call, but is in reality a completely different process.
You had stated:
But when the constructor is replaced with a function that returns void, this is legal.
However, I don't believe that it is: Here's a link to Compiler Explorer that demonstrates that your code fails to compile.
I've tested this with x86-64 clang 10.0.1, x86-64 gcc 10.2, and x64 msvc v19.24 and all three fail to compile. Here are the errors that they are each reporting:
clang: - error: constructor A must not return void expression
where the highlight is under the return keyword.
gcc: - error: return a value from a constructor
where the highlight is under the value 0.
msvc: - error C2534: A constructor cannot return a value
where the highlight is under the entire line of code for that constructor.
This statement from your questions seems a bit misleading... I don't see how it is legal at all!
There are only 3 things a constructor can do...
Early Return
Throw an exception
Return after }; at the end of the constructor's block or scope is reached and control flow is returned back to the caller.
All functions must have a return type, even those that don't return such as:
void print(){/*...*/ return;}
int add(int a, int b) { return (a+b); }
etc...
However, constructors and destructors are never declared with a type.
You will never see:
struct A{
void A() { return; } // Fails to compile
int A() { return 0; } // Fails to compile
void ~A() { return; } // Fails to compile
int ~A() {return 0; } // Fails to compile
};
There are NO types associated with ctors and dtors, at least not in c++.
Now if you remove the (void)0 within the code from Compiler Explorer you will see the foo(): and bar(): labels and their stack frames. You will also see the main: label and its stack frame. Yet you will see nothing for A. Now if you add an instance of A in main by instantiating it with an instance of the class object, you will see the change in the assembly code within main's stack frame as it is local to the main() function.
Here is clang's assembly:
Without A a being declared...
foo(): # #foo()
push rbp
mov rbp, rsp
pop rbp
ret
bar(): # #bar()
push rbp
mov rbp, rsp
xor eax, eax
pop rbp
ret
main: # #main
push rbp
mov rbp, rsp
xor eax, eax
mov dword ptr [rbp - 4], 0
pop rbp
ret
With A a; being declared...
foo(): # #foo()
push rbp
mov rbp, rsp
pop rbp
ret
bar(): # #bar()
push rbp
mov rbp, rsp
xor eax, eax
pop rbp
ret
main: # #main
push rbp
mov rbp, rsp
sub rsp, 16
mov dword ptr [rbp - 4], 0
lea rdi, [rbp - 8]
call A::A() [base object constructor]
xor eax, eax
add rsp, 16
pop rbp
ret
A::A() [base object constructor]: # #A::A() [base object constructor]
push rbp
mov rbp, rsp
mov qword ptr [rbp - 8], rdi
pop rbp
ret
Here's gcc's assembly:
Without:
foo():
push rbp
mov rbp, rsp
nop
pop rbp
ret
bar():
push rbp
mov rbp, rsp
mov eax, 0
pop rbp
ret
main:
push rbp
mov rbp, rsp
mov eax, 0
pop rbp
ret
With:
foo():
push rbp
mov rbp, rsp
nop
pop rbp
ret
bar():
push rbp
mov rbp, rsp
mov eax, 0
pop rbp
ret
A::A() [base object constructor]:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
lea rax, [rbp-1]
mov rdi, rax
call A::A() [complete object constructor]
mov eax, 0
leave
ret
Here's msvcs assembly:
Without:
void foo(void) PROC ; foo
ret 0
void foo(void) ENDP ; foo
int bar(void) PROC ; bar
xor eax, eax
ret 0
int bar(void) ENDP ; bar
main PROC
xor eax, eax
ret 0
main ENDP
With:
void foo(void) PROC ; foo
ret 0
void foo(void) ENDP ; foo
int bar(void) PROC ; bar
xor eax, eax
ret 0
int bar(void) ENDP ; bar
this$ = 8
A::A(void) PROC ; A::A, COMDAT
mov QWORD PTR [rsp+8], rcx
mov rax, QWORD PTR this$[rsp]
ret 0
A::A(void) ENDP ; A::A
a$ = 32
main PROC
$LN3:
sub rsp, 56 ; 00000038H
lea rcx, QWORD PTR a$[rsp]
call A::A(void) ; A::A
xor eax, eax
add rsp, 56 ; 00000038H
ret 0
main ENDP
As you can see from all 3 compilers when you do not have an instance of an object there is no generated assembly code. When you do have an instance of an object all compilers invoke call to A::A() or A::A(void)... Execution control enters into these constructors just like a function, however, they have no types because they are not actual functions, they are just treated like one...
They do have a stack frame, a scope, and a lifetime like a function, but these are only invoked when an object of the class's or struct's type is being declared. The assembly instructions for class constructors are only generated when an instance is being created.
They are not like a regular function where you could do this:
foo() {
A(); // this is not valid
A::A(); // this is not valid
}
However, this is valid:
foo() {
A a(); // Valid
}
Here the constructor is invoked on the object that is named a of type A.
I hope this helps to clarify why constructors or ctors don't have return types associated with them. The same thing goes for their destructors or dtors.
Edit
I think people were miss interpreting what I was trying to get at... I made a slight modification to the code: Maybe this will illustrate my intent more clearly...
Here's the C++ code:
struct A {
//A() { return (void)0; }
A() {};
};
void foo() {
A a;
return (void)0;
}
int bar() {
A a;
return 0;
}
int main() {
foo();
int baz = bar();
A a;
return 0;
}
And here's GCC's version of its Assembly:
foo(): # #foo()
push rbp
mov rbp, rsp
sub rsp, 16
lea rdi, [rbp - 8]
call A::A() [base object constructor]
add rsp, 16
pop rbp
ret
A::A() [base object constructor]: # #A::A() [base object constructor]
push rbp
mov rbp, rsp
mov qword ptr [rbp - 8], rdi
pop rbp
ret
bar(): # #bar()
push rbp
mov rbp, rsp
sub rsp, 16
lea rdi, [rbp - 8]
call A::A() [base object constructor]
xor eax, eax
add rsp, 16
pop rbp
ret
main: # #main
push rbp
mov rbp, rsp
sub rsp, 16
mov dword ptr [rbp - 4], 0
call foo()
call bar()
mov dword ptr [rbp - 8], eax
lea rdi, [rbp - 16]
call A::A() [base object constructor]
xor eax, eax
add rsp, 16
pop rbp
ret
Here's the udpated link to Compiler Explorer. And if you look closely at the generated assembly, When A::A() is called there is no information, nor any assembly code in regards to a type. When foo() is called its return type void is optimized away and when bar() is called there is assembly code to store it' return value.

C++ std::string initialization better performance (assembly)

I was playing with www.godbolt.org to check what code generates better assembly code, and I can't understand why this two different approaches generate different results (in assembly commands).
The first approach is to declare a string, and then later set a value:
#include <string>
int foo() {
std::string a;
a = "abcdef";
return a.size();
}
Which, in my gcc 7.4 (-O3) outputs:
.LC0:
.string "abcdef"
foo():
push rbp
mov r8d, 6
mov ecx, OFFSET FLAT:.LC0
xor edx, edx
push rbx
xor esi, esi
sub rsp, 40
lea rbx, [rsp+16]
mov rdi, rsp
mov BYTE PTR [rsp+16], 0
mov QWORD PTR [rsp], rbx
mov QWORD PTR [rsp+8], 0
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_replace(unsigned long, unsigned long, char const*, unsigned long)
mov rdi, QWORD PTR [rsp]
mov rbp, QWORD PTR [rsp+8]
cmp rdi, rbx
je .L1
call operator delete(void*)
.L1:
add rsp, 40
mov eax, ebp
pop rbx
pop rbp
ret
mov rbp, rax
jmp .L3
foo() [clone .cold]:
.L3:
mov rdi, QWORD PTR [rsp]
cmp rdi, rbx
je .L4
call operator delete(void*)
.L4:
mov rdi, rbp
call _Unwind_Resume
So, I imagined that if I initialize the string in the declaration, the output assembly would be shorter:
int bar() {
std::string a {"abcdef"};
return a.size();
}
And indeed it is:
bar():
mov eax, 6
ret
Why this huge difference? What prevents gcc to optimize the first version similar to the second?
godbolt link
This is just a guess:
operator= has a strong exception guarantee; which means:
If an exception is thrown for any reason, this function has no effect (strong exception guarantee).
(since C++11)
(source)
So while the constructor can leave the object in any condition it likes, operator= needs to make sure that the object is the same as before; I suspect that's why the call to operator delete is there (to clean up potentially allocated memory).

thread_local singleton performs lazy initialization by default?

I have the following code of a thread_local singleton:
struct Singl {
int& ref;
Singl(int& r) : ref(r) {}
~Singl() {}
void print() { std::cout << &ref << std::endl; }
};
static auto& singl(int& r) {
static thread_local Singl i(r);
return i;
}
int main() {
int x = 4;
singl(x).print();
int y = 55;
singl(y).print();
return 0;
}
This program prints twice the reference to x.
The compiler (gcc 8.1 on godbolt) seems to do a lazy initialization of the singleton object:
singl(int&):
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov rax, QWORD PTR fs:0
add rax, OFFSET FLAT:guard variable for singl(int&)::i#tpoff
movzx eax, BYTE PTR [rax]
test al, al
jne .L5
mov rax, QWORD PTR [rbp-8]
mov rdx, QWORD PTR fs:0
add rdx, OFFSET FLAT:singl(int&)::i#tpoff
mov rsi, rax
mov rdi, rdx
call Singl::Singl(int&)
mov rax, QWORD PTR fs:0
add rax, OFFSET FLAT:guard variable for singl(int&)::i#tpoff
mov BYTE PTR [rax], 1
mov rax, QWORD PTR fs:0
add rax, OFFSET FLAT:singl(int&)::i#tpoff
mov edx, OFFSET FLAT:__dso_handle
mov rsi, rax
mov edi, OFFSET FLAT:_ZN5SinglD1Ev
call __cxa_thread_atexit
.L5:
mov rax, QWORD PTR fs:0
add rax, OFFSET FLAT:singl(int&)::i#tpoff
leave
ret
Is this the default behaviour I can expect whenever I make multiple calls to the singl-function passing different arguments? Or is it possible that the singleton object might be initialized a second time on a subsequent call?
This is indeed guaranteed. static/thread_local local variables are initialized exactly once, when control reaches the declaration.
A few points to take note of:
If multiple threads are calling the function concurrently, only one will perform the initialization, and the others will wait. This is what the guard variables in the disassembly is doing.
If the initialization throws an exception, it is considered incomplete, and will be performed again the next time control reaches it.
In other words, they _just work_™.