Constructor implicitly deleted - c++

The related code is listed below, you could check it on https://godbolt.org/z/3GH8zD.
I could solve the complier compile error indeed.But i am not completely clear about the reason lie behind it.I would be grateful to have some help with this question.
struct A
{
int x;
A(int x = 1): x(x) {} // user-defined default constructor
};
struct F : public A
{
int& ref; // reference member
const int c; // const member
// F::F() is implicitly defined as deleted
};
int main()
{
F f; // compile error
}
Compiler compliains:
Could not execute the program
Compiler returned: 1
Compiler stderr
<source>:10:15: error: declaration does not declare anything [-fpermissive]
10 | const int; // const member
| ^~~
<source>: In function 'int main()':
<source>:16:9: error: use of deleted function 'F::F()'
16 | F f; // compile error
| ^
<source>:7:12: note: 'F::F()' is implicitly deleted because the default definition would be ill-formed:
7 | struct F : public A
| ^
<source>:7:12: error: uninitialized reference member in 'struct F'
<source>:9:14: note: 'int& F::ref' should be initialized
9 | int& ref; // reference member
| ^~~
The right code may be:
struct F
{
int& ref = x; // reference member
const int c = 1; // const member
// F::F() is implicitly defined as deleted
};

The class member int& ref needs to be bound to an int when an object of type F is created.
A compiler-generated default constructor would not know how to bind it, so the compiler merely deletes the default constructor.
In the second snippet you explicitly set the member. (You can do that from C++11).

Have a look at the Deleted implicitly-declared default constructor section in cppreference. It explains under which circumstances the default constructor is deleted:
T has a member of reference type without a default initializer. (since C++11)
T has a const member without user-defined default constructor or a default member initializer (since C++11).
That is, if you don't default initialize your const and reference member variables, the constructor will be deleted and it also explains why your second attempt "worked". In fact, it doesn't work until you make F inherit from A (I guess this was your intention). Otherwise, you can't assign A::x to F::ref.
Note: Even though F::ref=A::x is allowed, there is no point in doing that since F already has access to A::x due to inheritance.

To try to build on #Bathsheba answer.
Let's first understand what a reference is. A reference is essentially a pointer that can't be null this makes them very tricky to initialize.
Let's look at your second class:
struct F : public A
{
int& ref; // reference member
const int c; // const member
// F::F() is implicitly defined as deleted
};
If instead of defining it as int& ref; we define it as int ref; the compiler will say, I have no idea what ref is supoosed to be, so I'll stick some random value into it and be done. And so the default constructor just does ref = /*some num*/;
Let's say it was a pointer instead, i.e int* ref; Once again, the default constructor needs to initialize it to a value, for pointers the most logical value for default initialization is nullptr and so we get int* ref = nullptr;
But what if it is a reference? i.e int& ref;. We can;t initialize the variable to a number, because it is not a number it's a reference "i.e a pointer" to a number. But references can't be null so we can;t use nullptr either. So there is no valid default value initialization for the reference. The only reasonable thing is then to delete the default constructor because there does not exist any default value we can use to initialize one of its members.

Related

Declaring a const object in C ++ requires a user-defined default constructor. If I have a mutable member variable, why not?

In C++, to declare an object of a class that has a member variable as const, we must have a user-defined default constructor. The following code illustrates this.
class Some {
int value;
};
int main() {
// error: default initialization of an object of const type 'const Some'
// without a user-provided default constructor
const Some some;
return 0;
}
However, if a member variable owned by a class is qualified as mutable, the compiler will not report any errors. For reference, I compiled using the command clang++ -std=c++17 -stdlib=libc++ helloworld.cpp -o helloworld.out --debug. I wonder if this result is due to a bug in the compiler or according to the syntax defined in the C++ language.
class Some {
mutable int value;
};
int main() {
const Some some;
return 0;
}
Rewriting my comment as an answer, hope it could helps someone.
It makes no sense declaring a const object if it is not initialized in some form.
Consider the following code:
const int x;
clang says: error: default initialization of an object of const type 'const int'.
gcc would say: error: uninitialized const ‘x’ [-fpermissive]
The logic behind this is that there is no sense in this type of declaration.
The value of x can never change, and therefore this code would be unpredictable as x would be mapped to uninitialized memory.
In your example, adding the keyword mutable to value means that although the Some instance is constant when declared as:
const Some some;
It is still possible to change value at a later time.
For example:
some.value = 8;
This means it is possible to use this code in a predictable manner, since value can be set later, and there are no uninitialized constants.

Why/How does the C++ compiler understand this initialization of a class instance? [duplicate]

This question already has answers here:
Can I use identical names for fields and constructor parameters?
(6 answers)
Closed 1 year ago.
I figured out that it's possible to initialize the member variables with a constructor argument of the same name as show in the example below.
#include <cstdio>
#include <vector>
class Blah {
std::vector<int> vec;
public:
Blah(std::vector<int> vec): vec(vec)
{}
void printVec() {
for(unsigned int i=0; i<vec.size(); i++)
printf("%i ", vec.at(i));
printf("\n");
}
};
int main() {
std::vector<int> myVector(3);
myVector.at(0) = 1;
myVector.at(1) = 2;
myVector.at(2) = 3;
Blah blah(myVector);
blah.printVec();
return 0;
}
g++ 4.4 with the arguments -Wall -Wextra -pedantic gives no warning and works correctly. It also works with clang++. I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?
I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?
Yes. That is perfectly legal. Fully Standard conformant.
Blah(std::vector<int> vec): vec(vec){}
^ ^
| |
| this is the argument to the constructor
this is your member data
Since you asked for the reference in the Standard, here it is, with an example.
§12.6.2/7
Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified.
[Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
//^^^^ note this (added by Nawaz)
};
initializes X::r to refer to X::a,
initializes X::b with the value of the
constructor parameter i, initializes
X::i with the value of the constructor
parameter i, and initializes X::j with
the value of X::i; this takes place
each time an object of class X is
created. ]
[Note: because the
mem-initializer are evaluated in the
scope of the constructor, the this
pointer can be used in the
expression-list of a mem-initializer
to refer to the object being
initialized. ]
As you can see, there're other interesting thing to note in the above example, and the commentary from the Standard itself.
BTW, as side note, why don't you accept the parameter as const reference:
Blah(const std::vector<int> & vec): vec(vec) {}
^^^^const ^reference
It avoids unneccessary copy of the original vector object.
It is guaranteed always to work (I use it quite often). The compiler knows that the initializer list is of the form: member(value), and so it knows that the first vec in vec(vec) must be a member. Now on the argument to initialize the member, both members, arguments to the constructor and other symbols can be used, as in any expression that would be present inside the constructor. At this point it applies the regular lookup rules, and the argument vec hides the member vec.
Section 12.6.2 of the standard deals with initialization and it explains the process with paragraph 2 dealing with lookup for the member and paragraph 7 with the lookup of the argument.
Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified. [Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
};
One additional counter argument or perhaps just something to be aware of is the situation in which move construction is used to intialize the member variable.
If the member variable needs to be used within the body of the constructor then the member variable needs to be explcitly referenced through the this pointer, otherwise the moved variable will be used which is in an undefined state.
template<typename B>
class A {
public:
A(B&& b): b(std::forward(b)) {
this->b.test(); // Correct
b.test(); // Undefined behavior
}
private:
B b;
};
As others have already answered: Yes, this is legal. And yes, this is guaranteed by the Standard to work.
And I find it horrible every time I see it, forcing me to pause: "vec(vec)? WTF? Ah yes, vec is a member variable..."
This is one of the reasons why many, including myself, like to use a naming convention which makes it clear that a member variable is a member variable. Conventions I have seen include adding an underscore suffix (vec_) or an m_ prefix (m_vec). Then, the initializer reads: vec_(vec) / m_vec(vec), which is a no-brainer.

C++ default argument constructor vs inline initialisation priority

Is the variable here 1 or 2 and is this compiler or standard dependent?
class foo {
int val = 1;
foo(int bar = 2) : val(bar) {}
}
It is 2 unless you provide a different value when calling the constructor.
See cpp reference concerning non-static data member initialisation:
If a member has a default member initializer and also appears in the
member initialization list in a constructor, the default member
initializer is ignored.
this here : foo(int bar = 2) is called default argument,
and allows you to call a function without providing that argument.
How?
if you do:
foo(100) then val is initialized with 100
but if you ommit the parameter
foo( ) then val is initialized with 2
at the end, that declaration makes this init superfluous:
int val = 1;

member initializer lists in constructor with same-name variables [duplicate]

This question already has answers here:
Can I use identical names for fields and constructor parameters?
(6 answers)
Closed 1 year ago.
I figured out that it's possible to initialize the member variables with a constructor argument of the same name as show in the example below.
#include <cstdio>
#include <vector>
class Blah {
std::vector<int> vec;
public:
Blah(std::vector<int> vec): vec(vec)
{}
void printVec() {
for(unsigned int i=0; i<vec.size(); i++)
printf("%i ", vec.at(i));
printf("\n");
}
};
int main() {
std::vector<int> myVector(3);
myVector.at(0) = 1;
myVector.at(1) = 2;
myVector.at(2) = 3;
Blah blah(myVector);
blah.printVec();
return 0;
}
g++ 4.4 with the arguments -Wall -Wextra -pedantic gives no warning and works correctly. It also works with clang++. I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?
I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?
Yes. That is perfectly legal. Fully Standard conformant.
Blah(std::vector<int> vec): vec(vec){}
^ ^
| |
| this is the argument to the constructor
this is your member data
Since you asked for the reference in the Standard, here it is, with an example.
§12.6.2/7
Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified.
[Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
//^^^^ note this (added by Nawaz)
};
initializes X::r to refer to X::a,
initializes X::b with the value of the
constructor parameter i, initializes
X::i with the value of the constructor
parameter i, and initializes X::j with
the value of X::i; this takes place
each time an object of class X is
created. ]
[Note: because the
mem-initializer are evaluated in the
scope of the constructor, the this
pointer can be used in the
expression-list of a mem-initializer
to refer to the object being
initialized. ]
As you can see, there're other interesting thing to note in the above example, and the commentary from the Standard itself.
BTW, as side note, why don't you accept the parameter as const reference:
Blah(const std::vector<int> & vec): vec(vec) {}
^^^^const ^reference
It avoids unneccessary copy of the original vector object.
It is guaranteed always to work (I use it quite often). The compiler knows that the initializer list is of the form: member(value), and so it knows that the first vec in vec(vec) must be a member. Now on the argument to initialize the member, both members, arguments to the constructor and other symbols can be used, as in any expression that would be present inside the constructor. At this point it applies the regular lookup rules, and the argument vec hides the member vec.
Section 12.6.2 of the standard deals with initialization and it explains the process with paragraph 2 dealing with lookup for the member and paragraph 7 with the lookup of the argument.
Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified. [Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
};
One additional counter argument or perhaps just something to be aware of is the situation in which move construction is used to intialize the member variable.
If the member variable needs to be used within the body of the constructor then the member variable needs to be explcitly referenced through the this pointer, otherwise the moved variable will be used which is in an undefined state.
template<typename B>
class A {
public:
A(B&& b): b(std::forward(b)) {
this->b.test(); // Correct
b.test(); // Undefined behavior
}
private:
B b;
};
As others have already answered: Yes, this is legal. And yes, this is guaranteed by the Standard to work.
And I find it horrible every time I see it, forcing me to pause: "vec(vec)? WTF? Ah yes, vec is a member variable..."
This is one of the reasons why many, including myself, like to use a naming convention which makes it clear that a member variable is a member variable. Conventions I have seen include adding an underscore suffix (vec_) or an m_ prefix (m_vec). Then, the initializer reads: vec_(vec) / m_vec(vec), which is a no-brainer.

Initializing member variables using the same name for constructor arguments as for the member variables allowed by the C++ standard? [duplicate]

This question already has answers here:
Can I use identical names for fields and constructor parameters?
(6 answers)
Closed 1 year ago.
I figured out that it's possible to initialize the member variables with a constructor argument of the same name as show in the example below.
#include <cstdio>
#include <vector>
class Blah {
std::vector<int> vec;
public:
Blah(std::vector<int> vec): vec(vec)
{}
void printVec() {
for(unsigned int i=0; i<vec.size(); i++)
printf("%i ", vec.at(i));
printf("\n");
}
};
int main() {
std::vector<int> myVector(3);
myVector.at(0) = 1;
myVector.at(1) = 2;
myVector.at(2) = 3;
Blah blah(myVector);
blah.printVec();
return 0;
}
g++ 4.4 with the arguments -Wall -Wextra -pedantic gives no warning and works correctly. It also works with clang++. I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?
I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?
Yes. That is perfectly legal. Fully Standard conformant.
Blah(std::vector<int> vec): vec(vec){}
^ ^
| |
| this is the argument to the constructor
this is your member data
Since you asked for the reference in the Standard, here it is, with an example.
§12.6.2/7
Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified.
[Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
//^^^^ note this (added by Nawaz)
};
initializes X::r to refer to X::a,
initializes X::b with the value of the
constructor parameter i, initializes
X::i with the value of the constructor
parameter i, and initializes X::j with
the value of X::i; this takes place
each time an object of class X is
created. ]
[Note: because the
mem-initializer are evaluated in the
scope of the constructor, the this
pointer can be used in the
expression-list of a mem-initializer
to refer to the object being
initialized. ]
As you can see, there're other interesting thing to note in the above example, and the commentary from the Standard itself.
BTW, as side note, why don't you accept the parameter as const reference:
Blah(const std::vector<int> & vec): vec(vec) {}
^^^^const ^reference
It avoids unneccessary copy of the original vector object.
It is guaranteed always to work (I use it quite often). The compiler knows that the initializer list is of the form: member(value), and so it knows that the first vec in vec(vec) must be a member. Now on the argument to initialize the member, both members, arguments to the constructor and other symbols can be used, as in any expression that would be present inside the constructor. At this point it applies the regular lookup rules, and the argument vec hides the member vec.
Section 12.6.2 of the standard deals with initialization and it explains the process with paragraph 2 dealing with lookup for the member and paragraph 7 with the lookup of the argument.
Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified. [Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
};
One additional counter argument or perhaps just something to be aware of is the situation in which move construction is used to intialize the member variable.
If the member variable needs to be used within the body of the constructor then the member variable needs to be explcitly referenced through the this pointer, otherwise the moved variable will be used which is in an undefined state.
template<typename B>
class A {
public:
A(B&& b): b(std::forward(b)) {
this->b.test(); // Correct
b.test(); // Undefined behavior
}
private:
B b;
};
As others have already answered: Yes, this is legal. And yes, this is guaranteed by the Standard to work.
And I find it horrible every time I see it, forcing me to pause: "vec(vec)? WTF? Ah yes, vec is a member variable..."
This is one of the reasons why many, including myself, like to use a naming convention which makes it clear that a member variable is a member variable. Conventions I have seen include adding an underscore suffix (vec_) or an m_ prefix (m_vec). Then, the initializer reads: vec_(vec) / m_vec(vec), which is a no-brainer.