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&);
}
Related
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&);
};
#include <string>
#include <iostream>
#include <vector>
class HasPtrValue {
public:
HasPtrValue(const HasPtrValue& h): ps(new std::string(*h.ps)) { std::cout << "copy" << std::endl;}
HasPtrValue(const std::string &s = std::string()): ps(new std::string(s)) { std::cout << "string/default" << std::endl;}
~HasPtrValue() { delete ps; }
private:
std::string *ps;
};
using namespace std;
int main(){
string s = "stackoverflow";
vector<HasPtrValue> a(5, s);
}
The above code compiles fine outputting:
string/default
copy
copy
copy
copy
copy
This suggests to me the vector first directly initialises a temporary HasPtrValue object using the string object (doing HasPtrValue(s))and then copy-constructs the 5 elements from this temporary. How come, then, the following code does not compile:
int main(){
vector<HasPtrValue> a(5, "stackoverflow");
}
If it was directly initialising the HasPtrValue (doing HasPtrValue("stackoverflow")) then there would be no issue in the const string& constructor taking up the role of creating the temporary. I get the error;
error: no matching function for call to 'std::vector<HasPtrValue>::vector(int, const char [14])'|
I thought I'd try it with a simpler class that uses an int constructor and converts from a double:
class A{
public:
A(const int& a): x(a) { }
int x = 2;
};
int main(){
vector<A> a(5, 5.5);
}
Except this compiles fine. What part of the vector implementation prevents using a const char* conversion in the constructor?
Because it needs two user defined conversions, const char* -> std::string, and then std::string -> HasPtrValue, but only once user defined implicit conversion is permitted in an implicit conversion sequence.
13.3.3.1.2$1 User-defined conversion sequences [over.ics.user]
A user-defined conversion sequence consists of an initial standard
conversion sequence followed by a userdefined conversion (12.3)
followed by a second standard conversion sequence.
Note here only one level of user defined implicit conversion is legal. For your case this must be handled by explicit conversions; So you can:
vector<HasPtrValue> a(5, std::string("stackoverflow"));
int main(){
vector<HasPtrValue> a(5, string("stackoverflow"));
}
Your constructor requires std::string and "stackoverflow" is char array.
Alternatively you can define additional constructor accepting char[].
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.
Consider the following code.
#include <iostream>
using namespace std;
class Test
{
private:
int x,y;
public:
Test () {
cout <<" Inside Constructor "<<endl;
x=100;
}
explicit Test (const Test & t)
{
cout <<"Inside Copy Constructor "<<endl;
x = t.x;
}
void display()
{
cout <<" X is "<<x<<endl;
}
};
int main (int argc, char ** argv){
Test t;
t.display();
cout <<"--- Using Copy constructor "<<endl;
Test t2(t);
t2.display ();
Test t3=t2;
t3.display ();
}
Test (const Test & t) -> is a copy constructor
Question:
Is the same used as a "Conversion Operator" ?
Test t3 = t2 [ Here copy Constructor is treated as a conversion operator]
I am not sure if my understanding is correct?. Kindly correct me if i am wrong?
Test t3=t2;
Should never compile, if copy c-tor is explicit.
n3337 12.3.1/3
A non-explicit copy/move constructor (12.8) is a converting constructor. An implicitly-declared copy/move
constructor is not an explicit constructor; it may be called for implicit type conversions.
This quote appears to following question: Implicit copy constructor
So, in your case, it's not conversion constructor.
In C++, the term conversion implies two different types : source type and destination type.
Copy-constructor, by definition, involves only one type : source type and destination type are same. So it cannot be called a conversion function.
when you use T t3 = t2. It will call the assignment operator which you haven't define it.
The title basically says it all. I mainly want to do this so that I can create an object (say, a custom string object) that can initialize the parameters of other functions in other APIs. Here's an example of me trying to get a custom integer class to work:
#include <iostream>
using namespace std;
class test
{
public:
int member;
test(int i) : member(i) {}
friend int &operator=(int &i, test t);
};
int &operator=(int &i, test t)
{
return (i = t.member);
}
int main()
{
int i;
test t = 90;
cout << (i = t);
return 0;
}
Unfortunately I receive an error saying that operator= needs to be a member function. I understand the C++ standard's goal in preventing static and non-member overloads for the assignment operator from being implemented, but is there any other way to do this? Thanks for any help/suggestions!
This is not done with an assignment operator but with an overloaded typecast. This would make your main function work like expected:
#include <iostream>
using namespace std;
class test
{
public:
int member;
test(int i) : member(i) {}
operator int() const {return member;}
};
int main()
{
int i;
test t = 90;
cout << (i = t);
return 0;
}
What you are trying to do needs an conversion operator
operator int()
{
return this->member;
}
For the class you are trying to write(containing only integer members), You do not need to overload the = operator.
= operator is one of the member functions that is generated by the compiler by default for every class. Caveat is, it does a simple bit by bit copy(shallow copy) of class members, since you have only integers it should be good enough for you.
You would need to overload the = operator if you had dynamically allocated pointers as member functions, because in that case a shallow copy of those pointers would result in all the objects containing a member pointer pointing to the same dynamic memory location & if one of the object finishes it lifetime, other objects are left with a dangling pointer.
As #Tony, aptly points in out comments Shallow copy is usually bad but not always. See his comments for a scenario.
If at all you want to overload the assignment operator check out the Copy and Swap Idiom to do it right way.
You should also check out the Rule of Three.
Try this:
class test
{
public:
int member;
test(int i) : member(i) {}
operator int() {return this->member;}
};
int main(void)
{
int i;
test t = 90;
cout << (i = t);
return 0;
}
The assignment operator cannot be a friend function. The assignment operator can only be declared as a non-static member function. This is to ensure that it receives the L-value as its first operand. The same is true for the [], (), and -> operators. In your case, since int is an build-in type, you cannot use member function. You can implement operator int() to cast your user-defined type to int.