What is the GCC option for disabling assignment operator optimizations - c++

Let's start with this small example:
#include <vector>
#include <iostream>
using namespace std;
class A {
private:
A& operator =(const A&);
};
int main(void) {
vector<A> v;
v = { A() };
return 0;
}
Compilation of this code fails with the error message error: ‘A& A::operator=(const A&)’ is private. I have no idea why it needs the assignment operator so I tried to find out and changed the code to this:
#include <vector>
#include <iostream>
using namespace std;
class A {
public:
A& operator =(const A& a) { cout << "operator=" << endl; return *this; }
};
int main(void) {
vector<A> v;
v = { A() };
return 0;
}
Now the code compiles but when I execute it it does not output the debug message in the assignment operator implementation.
So the compiler wants the assignment operator but doesn't use it? I guess the compiler optimizes the assignment away somehow. Just like it optimizes the usage of move constructors (Which can be prevented with the option -no-elide-constructors). Is there a compiler option which can prevent assignment optimimzations? Or is there a different explanation why the compiler wants to have an accessible assignment operator but doesn't use it during runtime?

In C++03, types being stored in a container need to be CopyConstructible and Assignable. In C++11, requirements are relaxed and applied to the operations performed on the container.
class A needs to be CopyConstructible and Assignable because being stored in vector That's why you need public operator=
int main(void) {
vector<A> v;
v = { A() }; // Copy Constructor
A x;
x = v[0]; // operator=
return 0;
}

I little bit late but I still want to answer your question.
Your example shows a standard copy elision of C++. This is also discussed in another question.
That is, the compiler checks the correctness of your operation. You have to call a copy constructor right after default constructor to use vector and put your class inside, but call the default constructor only in order to improve performance.
C++ 11 solves the issue with the move constructor.

Related

Objects that can be initialized but not assigned

I need to create a class whose objects can be initialized but not assigned.
I thought maybe I could do this by not defining the assignment operator, but the compiler uses the constructor to do the assignment.
I need it to be this way:
Object a=1; // OK
a=1; // Error
How can I do it?
Making a const will do the trick
const Object a=1; // OK
Now you won't be able to assign any value to a as a is declared as const. Note that if you declare a as const, it is necessary to initialize a at the time of declaration.
Once you have declared a as const and also initialized it, you won't be able to assign any other value to a
a=1; //error
You can delete the assignment operator:
#include <iostream>
using namespace std;
struct Object
{
Object(int) {}
Object& operator=(int) = delete;
};
int main()
{
Object a=1; // OK
a=1; // Error
}
Alternative Solution
You can use the explicit keyword:
#include <iostream>
using namespace std;
struct Object
{
explicit Object(int) {}
};
int main()
{
Object a(1); // OK - Uses explicit constructor
a=1; // Error
}
Update
As mentioned by user2079303 in the comments:
It might be worth mentioning that the alternative solution does not prevent regular copy/move assignment like a=Object(1)
This can be avoided by using: Object& operator=(const Object&) = delete;
I hoped this would be so by not defining the assignment operator
This doesn't work because the copy assignment operator (which takes const Object& as parameter) is implicitly generated. And when you write a = 1, the generated copy assignment operator will be tried to invoke, and 1 could be implicitly converted to Object via converting constructor Object::Object(int); then a = 1; works fine.
You can declare the assignment operator taking int as deleted (since C++11) explicitly; which will be selected prior to the copy assignment operator in overload resolution.
If the function is overloaded, overload resolution takes place first, and the program is only ill-formed if the deleted function was selected.
e.g.
struct Object {
Object(int) {}
Object& operator=(int) = delete;
};
There're also some other solutions with side effects. You can declare Object::Object(int) as explicit to prohibit the implicit conversion from int to Object and then make a = 1 fail. But note this will make Object a = 1; fail too because copy initialization doesn't consider explicit constructor. Or you can mark the copy assignment operator deleted too, but this will make the assignment between Objects fail too.
How can I do it?
Option 1:
Make the constructor explicit
struct Object
{
explicit Object(int in) {}
};
Option 2:
delete the assignment operator.
struct Object
{
Object(int in) {}
Object& operator=(int in) = delete;
};
You can use both of the above options.
struct Object
{
explicit Object(int in) {}
Object& operator=(int in) = delete;
};
Option 3:
If you don't want any assignment after initialization, you can delete the assignment operator with Object as argument type.
struct Object
{
explicit Object(int in) {}
Object& operator=(Object const& in) = delete;
};
That will prevent use of:
Object a(1);
a = Object(2); // Error
a = 2; // Error
Deleted functions are available only from C++11 onwards, for older compilers you can make the assignment operator private.
struct Object
{
Object(int) {}
private:
Object& operator=(int);
};
Compiler will now throw error for
Object a=1; //ok
a=2; // error
But you can still do
Object a=1,b=2;
b=a;
Because the default assignment operator is not prevented from being generated by the compiler. So marking default assignment private will solve this issue.
struct Object
{
Object(int) {}
private:
Object& operator=(Object&);
};

Using copy constructor in assignment operator for a linked list

I need to implement a generic map. I decide to implement it with a linked list. I notice the copy constructor and the assignment operator are similar because they both create and copy linked list.
So I wonder if I can use the copy operator in the assignment operator or vice versa. Do you have an idea to do it?
In most cases if you've defined the copy constructor, it's already used when you do a copy assignment. That is to say the compiler implicitly uses your copy constructor to define copy assignment in the general case.
Example:
#include<iostream>
using namespace std;
struct copy_me {
int member;
copy_me() : member(0) {};
copy_me(const copy_me &other) : member(other.member) {
cout << "copy const called" << endl;
};
};
int main() {
copy_me me;
copy_me other = me;
return other.member;
}
Output:
copy const called
To answer your specific question:

Autogenerated default move constructor with declared destructor

C++11 Standard specifies that auto generated move constructors should not be generated for classes declaring destructors, but the following
code snippet builds and runs correctly:
#include <iostream>
class C
{
public:
~C() { std::cout << "Called C:~C" << std::endl; }
private:
std::string v;
};
int main()
{
C c1;
C c2 = std::move(c1);
return 0;
}
I can build with clang 4.2.1 and gcc 4.4.3. Am i missing something?
The initialization uses the implicitly defined copy constructor. In general a move will always fall back to a copy when the move constructor or move assignment operator cannot be used for some reason, since the copy constructor and copy assignment operator are always declared (although they can be deleted under some circumstances).
Brian's answer already explains why your code compiles. To properly check that move ctor is supressed just put a non copyable object into your class, for example std::unique_ptr:
class C
{
public:
~C() { std::cout << "Called C:~C" << std::endl; }
private:
std::string v;
std::unique_ptr<char> p;
};
Now gcc 5.1.0 produces this error:
error: use of deleted function ‘C::C(const C&)’
C c2 = std::move(c1);

std::make_shared, std::unique_ptr and move constructors

The following code compiles with clang 3.0/libc++:
#include <memory>
class Foo
{
public:
Foo()
: mem_(new int(10))
{
}
std::unique_ptr<int> mem_;
};
int main()
{
auto foo = std::make_shared<Foo>();
return 0;
}
But this one doesn't (std::string parameter added):
#include <memory>
#include <string>
class Foo
{
public:
Foo(const std::string& s)
: mem_(new int(10))
{
}
std::unique_ptr<int> mem_;
};
int main()
{
auto foo = std::make_shared<Foo>("aaa");
return 0;
}
Clang complains about the usage of a deleted constructor. To me, this makes no sense, since std::make_shared isn't supposed to copy the Foo instance, the only thing that would trigger the call to the (deleted) copy constructor of std::unique_ptr.
But lo and behold, as soon as I define a move constructor explicitly, it compiles.
#include <memory>
#include <string>
class Foo
{
public:
Foo(const std::string& s)
: mem_(new int(10))
{
}
Foo(Foo&& other)
: mem_(std::move(other.mem_))
{
}
std::unique_ptr<int> mem_;
};
int main()
{
auto foo = std::make_shared<Foo>("aaa");
return 0;
}
Now, questions:
Why does it compile in the first example but not the second?
Can std::make_shared copy/move the object while constructing it?
Why does adding a move constructor fix the problem? I don't recall that adding non-default constructor should suppress an implicit move constructor.
EDIT: Checked and all examples appear to compile fine with gcc 4.5.1 (via ideone.com), I suspect it's the case of a clang/libc++ bug, but questions 2 and 3 still stand, plus I'd like to know which compiler is more "correct".
Why does it compile in the first example but not the second?
This is a libc++ bug. I am working on a fix for it now...
Can std::make_shared copy/move the object while constructing it?
No, I don't believe it can.
Why does adding a move constructor fix the problem? I don't recall
that adding non-default constructor should suppress an implicit move
constructor.
In the version of clang you're using, implicit move constructors are not implemented yet.
Update
Fixed: http://llvm.org/bugs/show_bug.cgi?id=11616

Assignment vs Initialization in C++

I thought that constructors control initialization and operator= functions control assignment in C++. So why does this code work?
#include <iostream>
#include <cmath>
using namespace std;
class Deg {
public:
Deg() {}
Deg(int a) : d(a) {}
void operator()(double a)
{
cout << pow(a,d) << endl;
}
private:
int d;
};
int
main(int argc, char **argv)
{
Deg d = 2;
d(5);
d = 3; /* this shouldn't work, Deg doesn't have an operator= that takes an int */
d(5);
return 0;
}
On the third line of the main function, I am assigning an int to an object of class Deg. Since I don't have an operator=(int) function, I thought that this would certainly fail...but instead it calls the Deg(int a) constructor. So do constructors control assignment as well?
This is what's called implicit type conversion. The compiler will look to see if there's a constructor to directly change from the type you're assigning to the type you're trying to assign, and call it. You can stop it from happening by adding the explicit keyword in front of the constructor you wouldn't like to be implicitly called, like this:
explicit Deg(int a) : d(a) {}
Just to clarify JonM's answer:
For the line d = 3, an assignment operator is involved. 3 is being implicitly converted to a Deg, as JonM said, and then that Deg is assigned to d using the compiler-generated assignment operator (which by default does a member-wise assignment). If you want to prevent assignment, you must declare a private assignment operator (and do not implement it):
//...
private:
Deg& operator=(const Deg&);
}