Here's the code I have:
#include <iostream>
#include <functional>
struct Test {
struct Envelope {
const int x = 1;
int y = 2;
int z = 3;
};
Envelope mEnvelope;
struct Buffer {
Envelope mEnvelope;
} mBuffer;
std::function<Buffer()> func{[this] {
mBuffer.mEnvelope = mEnvelope;
return mBuffer;
}};
};
int main() {
Test test;
}
it says:
g++ -std=c++17 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In lambda function:
main.cpp:17:29: error: use of deleted function 'Test::Envelope& Test::Envelope::operator=(const Test::Envelope&)'
17 | mBuffer.mEnvelope = mEnvelope;
| ^~~~~~~~~
main.cpp:5:12: note: 'Test::Envelope& Test::Envelope::operator=(const Test::Envelope&)' is implicitly deleted because the default definition would be ill-formed:
5 | struct Envelope {
| ^~~~~~~~
main.cpp:5:12: error: non-static const member 'const int Test::Envelope::x', can't use default assignment operator
I've tried using a Copy Constructor:
Envelope(const Envelope &other) {
y = other.y;
}
or override the operator =
Envelope &operator=(Envelope &other) {
// self-assignment guard
if (this == &other) {
return *this;
}
y = other.y;
}
But the errors grown even more.
I need to copy only some "part" of the object.
This is just a test, of course the real object have lots of members fields, and some need to be ignored.
How to do it within a std::function<Buffer()>?
OK, since it might not be obvious what the problem is:
A copy assignment operator should usually have a const& parameter, not a & parameter.
You should then also provide the copy constructor of Envelope as you showed. First of all it has different behavior than the implicitly-generated one (which would copy over all the elements, not only y) and the generation of the implicit copy constructor when there is a user-defined copy assignment has been deprecated since C++11 and will probably be removed in a future standard iteration.
Then you will need to default the default constructor as well, since you have a user-defined constructor now:
Envelope() = default;
Furthermore, your copy assignment operator is declared to return a Envelope& (as it should), but you forgot to actually put a return statement in it at its end, so executing it will cause undefined behavior as is:
return *this;
You can create your own copy ctor/operator in order to only copy the info you want:
#include <iostream>
#include <functional>
struct Test {
typedef struct {
const int x;
int y;
int z;
} Envelope_t;
public:
Test():env({1,2,3}){}
Test(const Test & copy):env({copy.env.x,5,copy.env.z}) {}
Test& operator=(const Test& copy){
env.y=copy.env.y+7;
env.z=copy.env.z;
return *this;
}
void printValues() {
std::cout << "x:" << env.x << "\ny:" <<
env.y << "\nz:" << env.z << "\n\n";
}
private:
Envelope_t env;
};
int main() {
Test original;
original.printValues();
Test copyCtor(original);
copyCtor.printValues();
Test copyOp;
copyOp = copyCtor;
copyOp.printValues();
}
Related
test.cpp:
#include <iostream>
class MacroObject {
public :
MacroObject() = default;
MacroObject(const MacroObject&) = delete ;
MacroObject& operator=(const MacroObject&) = delete;
explicit MacroObject(MacroObject&&) = default;
MacroObject& operator=(MacroObject&&) = default;
int init(){return 0;}
int get(){return 0;}
};
MacroObject getObj(){
MacroObject obj;
obj.init();
return obj;
}
int main(){
MacroObject obj{getObj()};
std::cout << obj.get() << std::endl;
return 0;
}
I use this command with g++ 4.8.5:
g++ -std=c++11 test.cpp
I get this error message:
test.cpp: In function 'MacroObject getObj()':
test.cpp:19:8: error: use of deleted function 'MacroObject::MacroObject(const MacroObject&)'
return obj;
^
test.cpp:6:1: error: declared here
MacroObject(const MacroObject&) = delete ;
^
When I remove explicit, it is ok.
Why g++ uses the deleted copy-constructor and not the move-constructor?
By making the move constructor explicit you can't do
MacroObject x;
MacroObject y = std::move(x);
but can do
MacroObject y(std::move(x));
The return from the function, even in newer C++ versions where mandatory copy/move elision (NRVO) is in effect, tries to match the top version. Since that isn't a match, it checks the copy constructor. Since that is deleted, it stops with a failure.
It does not go on to try explicit versions. They are not candidates.
Making the copy and move constructors explicit makes the class useless with many standard library classes / functions so I recommend not making them explicit.
I would like to make a wrapper class (in this case a not null check, but it could be other checks).
Is there any way to extract member variable through operator() that would make it possible to move out the member variable. The use case is a std::unique_ptr<>
This is the use case
#include <memory>
struct S {
int x = 0;
S(int x): x(x) {}
};
template <typename Type>
class NotNull
{
public:
NotNull(Type value): m_value(std::move(value)) {}
operator Type&() // <--------------------------------- this is the question
{
assertIsNotNull();
return m_value;
}
typename std::pointer_traits<Type>::element_type *get() {
return m_value.get();
}
private:
void assertIsNotNull() {}
Type m_value;
};
And this is what needs to work
// Test code
int main() {
{
NotNull<S *> x {new S{10}};
auto y = x; // This works
delete y;
}
{
NotNull<std::shared_ptr<S>> x{std::make_shared<S>(10)};
auto y = x; // This works
}
{
NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
S* y = x.get(); // This does work, and needs to work as expected
// that is _not_ move the member
}
{
NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
auto y = std::move(x); // This copies the whole class
}
{
NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
std::unique_ptr<S> y = std::move(x); // <----------------- This does not work
}
}
The compiler seems to not understand that I want to convert to a unique_ptr inside of the std::move call.
error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = S; _Dp = std::default_delete<S>]'
57 | std::unique_ptr<S> y = std::move(x);
|
Compiler explorer link
Is it possible to use a class as a proxy for std::unique_ptr somehow and get this kind of syntax for moving out member variables.
PS. As you might have guessed I cannot rely on for example gsl::not_null, because this functionality does not exist there to my knowledge. DS.
The compiler understands that you want to convert to a unique_ptr& (i.e Type&) just fine. The problem arises when you assign the result of that conversion to your local unique_ptr object: since its an lvalue reference, the compiler tries to invoke the copy constructor (not the move constructor, which requires an rvalue reference), but since unique_ptr deleted its copy constructor, you get the error.
What you probably wanted to do in that line is to convert to a unique_ptr&& (i.e an rvalue reference). To do that, you can overload your conversion operator based on ref-qualifiers:
operator Type&() & // converts to lvalue ref if invoked on lvalue
{
assertIsNotNull();
return m_value;
}
operator Type&&() && // converts to rvalue ref if invoked on a temporary
{
assertIsNotNull();
return std::move(m_value);
}
This way, the conversion operator will convert to the same type of reference that it was invoked on (i.e lvalue of used from a normal variable, and rvalue if used on a temporary or moved object).
You can use reference qualifiers to make separate versions of a member function depending on whether the object is an lvalue or rvalue reference, like so:
#include <memory>
#include <iostream>
struct S {
int x = 0;
S(int x): x(x) {}
};
template <typename Type>
class NotNull
{
public:
NotNull(Type value): m_value(std::move(value)) {}
operator Type&()
{
assertIsNotNull();
return m_value;
}
auto get() &
{
std::cout << "lvalue \n";
return m_value.get();
}
auto get() &&
{
std::cout << "rvalue \n";
auto retval = m_value.get();
m_value.release();
return retval;
}
private:
void assertIsNotNull() {}
Type m_value;
};
int main()
{
{
NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
S* y = x.get(); // This does work, and needs to work as expected
// that is _not_ move the member
}
{
NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
std::unique_ptr<S> y = std::unique_ptr<S>(std::move(x).get()); // Is this what you want?
}
}
Question:
Is there a difference between the following initializations?
(A) What exactly is the second one doing?
(B) Is one more efficient than the other?
int variable = 0;
int variable = int();
This question also applies to other data types such as std::string:
std::string variable = "";
std::string variable = std::string();
Background:
I basically got the idea here (the second code sample for the accepted answer) when I was trying to empty out a stringstream.
I also had to start using it when I began learning classes and realized that member variable initializations had to be done in the constructor, not just following its definition in the header. For example, initializing a vector:
// Header.h
class myClass
{
private:
std::vector<std::string> myVector;
};
// Source.cpp
myClass::myClass()
{
for (int i=0;i<5;i++)
{
myVector.push_back(std::string());
}
}
Any clarity on this will be greatly appreciated!
Edit
After reading again, I realized that you explicitely asked about the default constructor while I provided a lot of examples with a 1 parameter constructor.
For Visual Studio C++ compiler, the following code only executes the default constructor, but if the copy constructor is defined explicit, it still complains because the never called copy constructor can't be called this way.
#include <iostream>
class MyInt {
public:
MyInt() : _i(0) {
std::cout << "default" << std::endl;
}
MyInt(const MyInt& other) : _i(other._i) {
std::cout << "copy" << std::endl;
}
int _i;
};
int main() {
MyInt i = MyInt();
return i._i;
}
Original (typo fixed)
For int variables, there is no difference between the forms.
Custom classes with a 1 argument constructor also accept assignment initialization, unless the constructor is marked as explicit, then the constructor call Type varname(argument) is required and assignment produces a compiler error.
See below examples for the different variants
class MyInt1 {
public:
MyInt1(int i) : _i(i) { }
int _i;
};
class MyInt2 {
public:
explicit MyInt2(int i) : _i(i) { }
int _i;
};
class MyInt3 {
public:
explicit MyInt3(int i) : _i(i) { }
explicit MyInt3(const MyInt3& other) : _i(other._i) { }
int _i;
};
int main() {
MyInt1 i1_1(0); // int constructor called
MyInt1 i1_2 = 0; // int constructor called
MyInt2 i2_1(0); // int constructor called
MyInt2 i2_2 = 0; // int constructor explicit - ERROR!
MyInt2 i2_3 = MyInt2(0); // int constructor called
MyInt3 i3_1(0); // int constructor called
MyInt3 i3_2 = 0; // int constructor explicit - ERROR!
MyInt3 i3_3 = MyInt3(0); // int constructor called, copy constructor explicit - ERROR!
}
The main difference between something like:
int i = int(); and int i = 0;
is that using a default constructor such as int() or string(), etc., unless overloaded/overridden, will set the variable equal to NULL, while just about all other forms of instantiation and declarations of variables will require some form of value assignment and therefore will not be NULL but a specific value.
As far as my knowledge on efficiency, neither one is "better".
The following code fails with gcc 4.8.0 (mingw-w64) with -O2 -std=c++11 -frtti -fexceptions -mthreads
#include <string>
class Param
{
public:
Param() : data(new std::string) { }
Param(const std::string & other) : data(new std::string(other)) { }
Param(const Param & other) : data(new std::string(*other.data)) { }
Param & operator=(const Param & other) {
*data = *other.data; return *this;
}
~Param() {
delete data;
}
Param & operator=(Param &&) = delete;
private:
std::string * data;
};
int main()
{
Param param;
param = Param("hop");
return 0;
}
With the error : error: use of deleted function 'Param& Param::operator=(Param&&)'
On the line :
param = Param("hop");
And compiles well if I remove the move assignment delete line.
There should be no default move assignment operator since there are user defined copy constructors, user defined copy assignment, and destructors, so deleting it should not affect the compilation, why is it failing?
And why is the allocation simply not using a copy assignment?
The function you deleted is exactly the assignment operator you try to use in main. By explicitly defining it as deleted you declare it and at the same time say using it is an error. So when you try to assign from an rvalue (Param("hop")), the compiler first looks whether a move assignment operator was declared. Since it was and is the best match, it tries to use it, just to find that it was deleted. Thus the error.
Here's another example of this mechanism which uses no special functions:
class X
{
void f(int) {}
void f(short) = delete;
};
int main()
{
X x;
short s;
x.f(s); // error: f(short) is deleted.
}
Removing the deleted f(short) will cause the compiler to select the non-deleted f(int) and thus compile without error.
I have conflict in below codes.
#include <iostream>
using std::cout;
using std::endl;
class TestApp {
public:
TestApp(int _x = 9) {
cout << "default constructor\n";
}
TestApp(const TestApp &app) {
cout << "Copy constructor\n";
}
~TestApp() {
cout << "destructor\n";
}
void setX(int _x) {
}
const TestApp &operator=(TestApp &obj) {
cout << "= operator\n";
return *this;
}
void setX(int _x) {
cout << "Inside setX\n";
}
};
int main() {
TestApp app1;
TestApp app2;
TestApp app3;
(app1 = app2) = app3; // 1
app1 = app2 = app3; // 2
app1.setX(3)
return 0;
}
I got this error: for line 1 main.cpp:38: error: passing ‘const TestApp’ as ‘this’ argument of ‘const TestApp& TestApp::operator=(TestApp&)’ discards qualifiers
However I can use app1.setX(3);
main.cpp:38: error: no match for ‘operator=’ in ‘app1 = app2.TestApp::operator=(((TestApp&)(& app3)))’
main.cpp:28: note: candidates are: const TestApp& TestApp::operator=(TestApp&)
and to make it working I should make operator= like:
TestApp &operator=(const TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 1
TestApp &operator=(TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 2
why if I remove const keyword it works? and after line 1 app1 object is not constant.
You may not assign constant objects. For example consider this simple code
const int x = 10;
x = 20;
The compiler will issue an error for the second statement because x is a constant object and may not be assigned.
The same is valid for statement
(app1 = app2) = app3;
here expression (app1 = app2) returns constant reference that may not be assigned,
A constant reference does not mean that the object itself that it refers to is constant. Consider the following example
int x = 10;
const int &rx = x;
x = 20;
rx = 30;
Though rx is defined as constant reference you may change the object x itself. You may not use the reference to assign object x, so the compiler will issue an error for the last statement.
We use constant references very often in parameter declarations of functions that to prevent changing of objects they refer to inside the functions. For example
void f( const int &x )
{
x = 20; // compilation error
}
int x = 10;
f( x );
So defining a constant reference to a non-constant object does not makes the object itself constant. It only prevents to change the object using this reference.
And you need to define only one copy assignment operator
TestApp &operator=(const TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 1
There is no any need to define the copy assignment operator as
TestApp &operator=(TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 2
if you are not going to change the right operand. So it is better to define it as a constant reference const TestApp &obj
Of course you may have these two operators together but there is no any sense to have the second operator.
On the other hand you may not have only the second operator. In this case you will be unable to use constant objects that assign them to other objects.
The correct way to provide an assignment operator is to declare it as follows:
TestApp &operator=(const TestApp &obj) {
cout << "= operator\n";
return *this;
}
Note that there is only one const in front of the right hand side operand, the operator itself and its return value are not declared const.
It is wrong to declare the operator const because the assignment operator is meant to modify the this object.
And it unnecessarily constrains the use of the operator to return a const reference, because the caller already provided you with a non-const reference. Consequently, the caller already has non-const access to the object, so returning a const reference unnecessarily stops him from reusing the return value in a non-const context
This is what happens when you do the double assignment app1 = app2 = app3;: It is evaluated as app1 = (app2 = app3);, so the return value of one assignment is passed as the right hand side parameter to the next assignment. The non-const reference returned by the first assignment can implicitly be converted to a const reference, so this works fine.
If your compiler complains about your line 2 with the declaration given above, then your compiler is to blame. I have checked the following code with gcc 4.7.2, and it works fine:
class Foo {
public:
Foo() {};
Foo(const Foo& other) {}
Foo& operator=(const Foo& other) { return *this; }
};
int main() {
Foo foo1, foo2, foo3;
(foo1 = foo2) = foo3;
foo1 = foo2 = foo3;
}