Okay so I have a class that has 'weak typing' I.E. it can store many different types defined as:
#include <string>
class myObject{
public:
bool isString;
std::string strVal;
bool isNumber;
double numVal;
bool isBoolean;
bool boolVal;
double operator= (const myObject &);
};
I would like to overload the assignment operator like this:
double myObject::operator= (const myObject &right){
if(right.isNumber){
return right.numVal;
}else{
// Arbitrary Throw.
throw 5;
}
}
So that I can do this:
int main(){
myObject obj;
obj.isNumber = true;
obj.numVal = 17.5;
//This is what I would like to do
double number = obj;
}
But when I do that, I get:
error: cannot convert ‘myObject’ to ‘double’ in initialization
At the assignment.
I have also tried:
int main(){
myObject obj;
obj.isNumber = true;
obj.numVal = 17.5;
//This is what I would like to do
double number;
number = obj;
}
To which I get:
error: cannot convert ‘myObject’ to ‘double’ in assignment
Is there something I am missing? or is it simply not possible to do a conversion like that by overloading operator=.
Overloading operator= changes the behaviour when assigning to objects of your class type.
If you want to provide implicit conversion to other types you need to supply a conversion operator, e.g.
operator double() const
{
if (!isNumber)
throw something();
return numVal;
}
What you really want are conversion operators.
operator double() const { return numVal; }
operator int() const { ...
That said, you'd probably like boost::variant.
for that to work, you need to implement a conversion operator from your object to something that can be converted into a double
The return value of operator=() cannot be used as you have tried to demonstrate. If you think of the overloaded operator as a function in it's own right, it may make more sense.
For example:
int main() {
myObject obj, obj2;
obj.isNumber = true;
obj.numVal = 17.5;
obj2.operator=(obj); // equivalent to obj2 = obj
}
The reason number = obj; doesn't work is because you've defined myObject::operator=(), whereas number would be using double::operator=() (okay, technically there is no double::operator=() since it's a fundamental type and not a class...just work with me here).
An interesting note is that this function behaves like any other function in that the return value (return right.numval;) is ignored when it's not used. However, the return value can be assigned or used like the return value of any other function, so if you really wanted you could do something like this:
int main() {
myObject obj, obj2;
obj.isNumber = true;
obj.numVal = 17.5;
double number;
// number = obj; still won't work.
number = obj2 = obj; // equivalent to number = obj2.operator=(obj)
}
This is only so useful. As others have mentioned, you really want to look into conversion operators when trying to assign myObject objects to fundamental types.
To make a class assignable to a double, operator= must be defined differently.
double operator=(myclass&) is wrong.
What would work, is a friend operator= outside your class, which accepts double and myclass&.
Related
Im trying to figure how such an operator works:
I found nothing about it searching the web...
Is it used as a casting between types?
Why do I need the = operator (in line a = b) instead of a b ?
What other uses does it have?
thanks
class A{
int a;
};
class B{
int b;
operator A() { return A(); }
};
int main(){
A a;
B b;
a = b;
return 0;
}
What you are looking at is a user defined conversion operator. IT has plenty of uses, for example. Consider a smart pointer:
class SmartPointer {
// Constructor destructor, operator* and & etc ..
}
If it were a raw pointer, we could check for it being nullptr like this:
if (ptr) {
ptr->do_something();
}
So how can we achieve the same with a smart pointer? We can define operator bool.
Another example could be something like a units class:
class Meters {
...
}
What if we want to be able to achieve this:
void some_operation(double meters);
Meters m{10};
some_operation(m);
Well we can define a conversion operator:
Meters::operator double() {
return _meters;
}
Remember when you are looking into this to check if you need the explicit specifier, it is likely that you will want to use this for most conversions.
In the class below,
Why would you make the operators explicit. I thought that explicit was to prevent implicit calling of constructors?
class Content
{
public:
virtual ~Content() = 0;
virtual explicit operator float&();
virtual explicit operator long long&();
virtual explicit operator std::string&()
}
I thought that explicit was to prevent implicit calling of
constructors?
Since C++11 it also applies to user-defined conversions (a.k.a. the cast operator).
Why would you make the operators explicit
Used in this context, the explicit keyword makes the conversion eligible only for direct-initialization and explicit conversions. See here under [class.conv.fct¶2]:
A conversion function may be explicit ([dcl.fct.spec]), in which case
it is only considered as a user-defined conversion for
direct-initialization ([dcl.init]). Otherwise, user-defined
conversions are not restricted to use in assignments and
initializations.
This aids you in making sure the compiler doesn't try the conversion against your intention, so that you have to explicitly cast it yourself, leaving less room for error. Example:
struct Foo
{
explicit operator int() {return 0;}
operator int*() {return nullptr;}
};
int main()
{
Foo foo;
//int xi = foo; // Error, conversion must be explicit
int i = static_cast<int>(foo); // OK, conversion is explicit
int* i_ptr = foo; // OK, implicit conversion to `int*` is allowed
int i_direct(foo); // OK, direct initialization allowed
int* i_ptr_direct(foo); // OK, direct initialization after implicit conversion
return 0;
}
It can also help resolve ambiguity in cases where multiple conversion options apply, leaving the compiler without a criteria for deciding which one to choose:
struct Bar
{
operator int() {return 1;}
operator char() {return '1';}
};
int main()
{
Bar bar;
//double d = bar; // Error, implicit conversion is ambiguous
return 0;
}
Add explicit:
struct Bar
{
operator int() {return 1;}
explicit operator char() {return '1';}
};
int main()
{
Bar bar;
double d = bar; // OK, implicit conversion to `int` is the only option
return 0;
}
Consider the following:
struct Content
{
operator float() { return 42.f; }
friend Content operator+(Content& lhs, float) { return lhs; }
};
int main()
{
Content c{};
c + 0; // error: ambiguous overload for 'operator+'
}
Here, the compiler cannot choose between operator+(Content&, float) and operator+(float, int). Making the float operator explicit resolves this ambiguity*:
c + 0; // operator+(Content&, float)
or
static_cast<float>(c) + 0; // operator+(float, int)
*) provided it makes sense to prefer one over the other.
The other answers cover how it works, but I think you should be told why it was added to C++.
A smart pointer usually has a conversion to bool so you can do this:
std::shared_ptr<int> foo;
if (foo) {
*foo = 7;
}
where if(foo) converts foo to bool. Unfortunately:
int x = foo+2;
converts foo to bool, then to int, then adds 2. This is almost always a bug. This is permitted because while only one user defined conversion is done, a user defined conversion followed by a built in conversion can silently occur.
To fix this programmers would do crazy things like add:
struct secret {
void unused();
};
struct smart_ptr {
using secret_mem_ptr = void(secret::*)();
operator secret_mem_ptr() const { return 0; }
};
and secret_mem_ptr is a secret pointer to member. A pointer to member has a built in conversion to bool, so:
smart_ptr ptr;
if (!ptr) {
}
"works" -- ptr is convert to secret_mem_ptr, which then is convered to bool, which is then used to decide which branch to take.
This was more than a bit of a hack.
They added explicit on conversion operators to solve this exact problem.
Now:
struct smart_ptr {
explicit operator bool() const { return true; }
};
doesn't permit:
smart_ptr ptr;
int x = 3 + ptr;
but it does permit:
if (ptr) {
}
because the rules were hand-crafted to support exactly that use case. It also doesn't permit:
bool test() {
smart_ptr ptr;
return ptr;
}
here, you have to type:
bool test() {
smart_ptr ptr;
return (bool)ptr;
}
where you explicitly convert ptr to bool.
(I am usually really against C-style casts; I make an exception in the case of (bool)).
You would use it if you wanted a Content object never to be implicitly converted to (say) a float. This could happen in the following way:
void f( float f );
....
Content c;
f( c ); // conversion from Content to float
Without the explicit qualifier, the conversion happens implicitly; with it, you get a compilation error.
Silent, implicit conversions can be the source of a lot of confusion and/or bugs, so it's generally better to make the operators explicit , or probably better yet to provide named functions, such as ToFloat, which tell the reader exactly what is going on.
I have some special numeric class. The class can be constructed by a double:
struct FancyDouble{
FancyDouble& operator = (double v){
this->value = v;
return *this;
}
};
FancyDouble myFancyDouble = 5.0;
The class provides an (implicit) conversion from double to FancyDouble. Is there a way to do the opposite? So when I use a FancyDouble in a numeric expression, I'd like to write:
double a = 123;
double b = b * myFancyDouble;
I had a look into the reference and I think it's not possible, but I'm not absolutely sure.
Yes, you can. They are called conversion operators:
struct FancyDouble
{
// your things here
operator double() { return this->value; }
};
You can have a conversion to any type, not just built-in types. They are also typically marked const (though I didn't do it here to show the core concept).
Note that your struct (as shown) doesn't exactly allow implicit conversions from double to FancyDouble; your operator= only means that you can use a double on the right-hand side of an assignment, but if you have a FancyDouble function parameter, it won't work since what you would need there is a constructor that accepts a double, not an assignment operator.
void foo(FancyDouble fd);
foo(4.4); // does not work because you can't construct a FancyDouble from a double
// even though you have an assignment operator
You would do something like this:
struct FancyDouble
{
// your things here
FancyDouble(double d) { /* initialization here */ }
};
With this, you would only need an operator= that accepts a FancyDouble instead of a double, and the conversion would be done automatically:
struct FancyDouble
{
// your things here
FancyDouble(double d) { /* initialization here */ }
FancyDouble& operator=(FancyDouble fd) { /* assignment here */ }
};
FancyDouble fd = 4.2; // works
fd = 5.5; // also works, because FancyDouble can be constructed implicitly from a double
How would I overload the = operator in a way that I could execute
int someInt;
MyClass instanceOfMyClass;
someInt = instanceOfMyClass;
Where MyClass contains an integer named number?
You can't overload the operator= for the type int. What you are really looking for is the conversion operator operator int() for your MyClass. In your case, considering x to be the private member of your class:
operator int() { return x; }
I would avoid doing this in most cases as it can be difficult to tell what exactly is happening. That said:
class MyClass {
public:
operator int() { return number; }
private:
int number;
};
This creates an (implicit) conversion operator for your class.
I have a CCounter class which holds and integer value protected by mutex. I've defined several operators like post/pre inc/dec returning an integer so I can do:
CCounter c(10);
int i = c++;
but what do I do with a simple assignment like i = c ? I tried to define friend operator= but it gives me
operator=(int&, const CCounter&)’ must be a nonstatic member function
error. Please, advise. Thanks.
You need to define a casting operator that casts from CCounter to int. Add this member to your class:
operator int() const {
return ...;
}
As you have found out, the assignment operator must be a member function of a class. As ints are not classes, you can't write operator=() for them. The alternative, as others have pointed out is to write a function that converts to an int. I would strongly suggest you write a named function like ToInt() to do this, rather than using a conversion operator, which can be the source of non-obvious bugs.
G'day,
Shouldn't you be defining an accessor function instead if you're just "getting" the current value of the counter?
Something like:
int GetCounter();
Anything else is sort of disguising the intention of what you're trying to do. IMHO Natch! (-:
HTH
cheers,
You need to define operator int() to allow the conversion of your class to an int. For example:
class CCounter
{
public:
CCounter(int val) : m_val(val)
{
}
operator int() const
{
return m_val;
}
private:
int m_val;
};
int main(int argc,char *argv[])
{
CCounter c(10);
int n = c;
std::cout<<n<<"\n";
return 0;
}
As said use the int() operator. Here a code snippet :
#include <iostream>
class CCounter
{
public:
CCounter(int i = 0) : _count(i) {}
operator int() { return _count; }
private:
int _count;
};
int main()
{
CCounter counter(4);
int c = counter;
std::cout << "Counter = " << c << std::endl;
return 0;
}
You said:
"I've defined several operators like post/pre inc/dec returning an integer".
Now that other answers provided you with a generic way to convert the object to an integer, I would recommend that you change these other operators so that they behave as typically expected.
For instance, pre increment typically returns a reference to the object itself, and post increment typically returns a temporary copy of the original object (prior to the incrementation).
CCounter& operator++() {
++m_val;
return *this;
}
CCounter operator++(int) {
CCounter tmp(*this);
++m_val;
return tmp;
}
Although you have been given a valid solution, I would also consider simply creating a normal function which returns int, such as int GetValue() const, to improve readability and ease of maintenance. Of course this is highly subjective.
#include<iostream>
using namespace std;
class CA {
public:
int a;
CA(int x):a(x)
{
}
operator int() const {
return a;
}
void operator ()() {
}
};
void main(){
CA obj = 100;
int k = obj;
obj();
}