C++: struct initialization in presence of constructor - c++

I have question on C++ behavior when initializing structures using a list. For example, the following code behaves the same in C and C++. The list initializes x:
struct X {
int x;
};
int main(int argc, char *argv[])
{
struct X xx = {0};
return 0;
}
Now, if I add a constructor, I find out through testing that the constructor is called instead of the simple initialization of the x member:
#include <iostream>
using namespace std;
struct X {
int x;
X(int);
};
X::X(int i)
{
cout << "X(int i)" << endl;
x = i;
}
int main(int argc, char *argv[])
{
struct X xx = {0};
return 0;
}
Output:
X(int i)
Is the behavior of C++ with an identical constructor (as above) to override the simple list initialization? Giving my testing that is what appears to happen.
Thanks!

The following syntax:
X xx = {0};
is just a form of copy-list initialization. This has the effect of invoking the constructor of X, as you observed.
The name of this initialization comes from the fact that this looks like a list is being copied, but just the regular constructor is invoked. Note that this will only consider implicit constructors.
Also the elaborated-type-specifier struct is not necessary in c++, in the declaration of xx.
Note that if you don't provide a constructor such as X(int) (as you did in the first example), then the declaration of xx will instead do aggregate initialization.

Related

Undefined behaviour of Designated initializers in C++

The following C++20 program is accepted without any warning in all compiles I have tried:
struct A { const int & x = z; int y = x; int z; };
int main()
{
return A{.z=3}.y;
}
https://gcc.godbolt.org/z/nqb95zb7c
But every program returns some arbitrary value. Is it right to assume that this is undefined behavior?
Members are initialized in the order they appear in the class definition, hence the designated initializer is not that relevant, and also this
struct A {
const int & x = z;
int y = x; // <- read of indeterminate value !
int z = 42; // <- doesn't really matter because y is initialized before !
};
int main() {
return A{}.y;
}
is undefined for the same reason.
See also the example from cppreference:
struct A {
string str;
int n = 42;
int m = -1;
};
A{.m=21} // Initializes str with {}, which calls the default constructor
// then initializes n with = 42
// then initializes m with = 21
The example is actually to illustrate something else, but it also shows how members are initialized in order.

Using a non static value as default argument in a function

Is there a nice way to have a non static value as default argument in a function? I've seen some older responses to the same question which always end up in explicitly writing out the overload. Is this still necessary in C++17?
What I'd like to do is do something akin to
class C {
const int N; //Initialized in constructor
void foo(int x = this->N){
//do something
}
}
instead of having to write
class C {
const int N; //Initialized in constructor
void foo(){
foo(N);
}
void foo(int x){
//do something
}
}
which makes the purpose of the overload less obvious.
One relatively elegant way (in my opinion) would be to use std::optional to accept the argument, and if no argument was provided, use the default from the object:
class C {
const int N_; // Initialized in constructor
public:
C(int x) :N_(x) {}
void foo(std::optional<int> x = std::nullopt) {
std::cout << x.value_or(N_) << std::endl;
}
};
int main() {
C c(7);
c.foo();
c.foo(0);
}
You can find the full explanation of what works/doesn't work in section 11.3.6 of the standard. Subsection 9 describes member access (excerpt):
A non-static member shall not appear in a default argument unless it
appears as the id-expressionof a class member access expression
(8.5.1.5) or unless it is used to form a pointer to member
(8.5.2.1).[Example:The declaration of X::mem1()in the following example
is ill-formed because no object is supplied for the non-static
memberX::a used as an initializer.
int b;
class X {
int a;
int mem1(int i = a);// error: non-static memberaused as default argument
int mem2(int i = b);// OK; useX::b
static int b;
};

How to initialize structs that are members of a class in c++98?

The application is written in C++98, and I'm not in position to change it currently. The situation goes like this:
struct foo
{
int x;
int y;
int z;
}
class myClass
{
public:
myClass();
struct foo bar;
int baz;
};
myClass::myClass() :
bar({0}),
baz(0)
{
//something else.
}
int main(int argc, char** argv)
{
myClass* thing = new myClass();
cout << thing.bar.z << endl; //want a guaranteed zero here.
}
The above works, but produces:
warning: extended initializer lists only available with -std=c++11 or -std=gnu++11.
which is fully expectable. I can't do bar({0}) in the initializer list with c++98. Question remains what is the proper way to do it.
I can think of a couple ways that will work - memset, assign values field by field in constructor, assign using default copy constructor of the struct, etc. I don't know which way is considered 'most correct' in this situation though.

Can copy elision be perfomed in aggregate initialization in c++17?

Given:
//C++17
#include <string>
struct Foo {
int i;
std::string str;
};
int main() {
Foo foo{1, std::string("Hello, world!")};
}
Can Foo::i and Foo::str be directly initialized from 1 and std::string(...) instead of being copied into them, and explain why can/can't using C++17 standard(probably some code for testing purpose)?
If they can't, how many copies are required?
Aggregate initialization basically performs element-wise copy-initialization. So this:
struct Foo {
int i;
std::string str;
};
Foo foo{1, std::string("Hello, world!")};
does the same initializations as:
int i = 1;
std::string str = std::string("Hello, world!");
And we have a new rule in C++17 that says that:
If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object. [ Example: T x = T(T(T())); calls the T default constructor to initialize x. — end example ]
which means that the second initialization must behave as if you'd written:
std::string str("Hello, world!");
That is, zero copies.
A nice demonstration of the new rule is the following example:
struct X {
X(int ) { }
X(X&& ) = delete;
};
struct Y {
X x;
};
int main() {
Y y{X{4}}; // ill-formed in C++14 due to deleted move ctor
// ok in C++17, no move required
}

C++ Basic Template Class Compilation [duplicate]

I am trying to understand how default constructor (provided by the compiler if you do not write one) versus your own default constructor works.
So for example I wrote this simple class:
class A
{
private:
int x;
public:
A() { std::cout << "Default constructor called for A\n"; }
A(int x)
{
std::cout << "Argument constructor called for A\n";
this->x = x;
}
};
int main (int argc, char const *argv[])
{
A m;
A p(0);
A n();
return 0;
}
The output is :
Default constructor called for A
Argument constructor called for A
So for the last one there is another constructor called and my question is which one and which type does n have in this case?
A n();
declares a function, named n, that takes no arguments and returns an A.
Since it is a declaration, no code is invoked/executed (especially no constructor).
After that declaration, you might write something like
A myA = n();
This would compile. But it would not link! Because there is no definition of the function n.
A n();
could be parsed as an object definition with an empty initializer or a function declaration.
The language standard specifies that the ambiguity is always resolved in favour of the function declaration (§8.5.8).
So n is a function without arguments returning an A.
For the last one NO constructor gets called.
For that matter no code even gets generated. All you're doing is telling (declaring) the compiler that there's a function n which returns A and takes no argument.
No there is not a different constructor.
A n();
is treated as a declaration of function taking no arguments and returning A object. You can see this with this code:
class A
{
public:
int x;
public:
A(){ std::cout << "Default constructor called for A\n";}
A(int x){
std::cout << "Argument constructor called for A\n";
this->x = x;
}
};
int main(int argc, char const *argv[])
{
A m;
A p(0);
A n();
n.x =3;
return 0;
}
The error is:
main.cpp:129: error: request for member ‘x’ in ‘n’, which is of non-class type ‘A()’