C++ Templates and operator overloading - c++

I am learning templates and operator overloading. I have written some code but I am confused... Let me explain...
template <class T>
class tempType
{
public:
T value;
bool locked;
tempType():locked(false)
{
value = 0;
}
T operator=(T val)
{
value=val;
return value;
}
tempType<T> operator=(tempType<T> &val)
{
value=val.value;
return *this;
}
operator T()
{
return value;
}
};
And I did...
int main(void)
{
tempType<int> i;
tempType<bool> b;
tempType<float> f;
i.value = 10;
i = i + f;
return 0;
}
What code I need to write in order to execute
tempType<T> operator=(tempType<T> &val){}
Also, I why operator T() is required?

Unless you implement move semantics, operator= should always take a const & reference to the source value. It should also return a reference to the modified object.
tempType & operator=(T const & val)
{
value=val;
return * this;
}
operator T is an implicit conversion function which allows any tempType object to be treated as an object of its underlying type T. Be careful when specifying implicit conversions that they won't conflict with each other.
An implicit conversion function usually shouldn't make a copy, so you probably want
operator T & ()
{
return value;
}
operator T const & () const
{
return value;
}
Given these, you shouldn't need another overload of operator = because the first overload will simply be adapted by the conversion function to a call such as i = b;.
If a series of conversions will result in the operator=(T const & val) being called, you should avoid also defining operator=(tempType const & val) because the overloads will compete on the basis of which conversion sequence is "better," which can result in a brittle (finicky) interface that may refuse to do seemingly reasonable things.

I think I know all the answers so I might as well post full response.
To override default operator= you should declare it as tempType<T> operator=(const tempType<T> &val){}. Now you need to call the method explicitly via i.operator=(other_i).
If you correct the declaration you can use it like this:
tempType<int> i;
tempType<int> other_i;
i = other_i; // this is what you just defined
The operator T() is called a conversion operator. It is kind of reverse or counter part of the conversion constructor which in your case would be tempType(const &T value).
It is used to convert a class object into a given type. So in your case you would be able to write:
tempType<int> i;
int some_int;
some_int = i; // tempType<int> gets converted into int via `operator int()`

template <class T>
class tempType
{
public:
T value;
bool locked;
tempType() : value(), locked(false)
{
value = 0;
}
//althought legal, returning something different from tempType&
//from an operator= is bad practice
T operator=(T val)
{
value=val;
return value;
}
tempType& operator=(const tempType &val)
{
value=val.value;
return *this;
}
operator T()
{
return value;
}
};
int main(void)
{
tempType<int> a;
tempType<int> b;
a = b;
return 0;
}
in the code, a = b calls the operator.
As for the second question, the operator T() is not needed "by default". In your example, it is used when you write i+f:
i is converted to an int
f is converted to a float
the operation (+) is performed
T tempType<int>::operator=(T val) is called for the assignement

Related

Can assignment operator be overloaded to return the value of a property of a class?

I wanted to return the value of a property of a class using assignment operator. I tried to fulfill this purpose. I searched a lot on the web but all of the websites I visited talked about how to overload assignment operator to do like a copy constructor of a class like this: class_t& operator=(class_t&);. Can anybody help me overload this operator to return the value of a property of a class?
This is my code:
class A_t
{
private:
int value = 0;
public:
int operator = (A_t); // I failed to overload assignment operator for this
A_t& operator = (int); // I succeeded to overload assignment operator for this
int Value();
void setValue(int);
};
A_t& A_t::operator = (int value)
{
this->setValue(value);
return *this;
}
int operator = (A_t &data)
{
return data.value;
}
int A_t::Value() { return this->value; }
void A_t::setValue(int data) { this->value = data; }
int main()
{
A_t object = 3;
int value = object; // Error: cannot convert 'A_t' to 'int' in initialization
cout << value << endl;
return 0;
}
You can’t overload operator = for this. What you can do instead is overload the implicit conversion-to-int operator inside the class:
operator int() const { return value; }
However, think carefully whether this is actually a good idea in your case. Implicit conversions should usually be avoided at all cost, because it’s very error-prone (many smart people think C++ shouldn’t allow defining custom implicit conversions at all!).
For this, you need an int operator for your class which returns the variable when being assigned to an integer. Plus the class missed the constructor required for A_t object = 3;. The corrected class looks like this,
class A_t
{
private:
int value = 0;
public:
//int operator = (A_t); <-- You dont need this.
A_t& operator = (int); // I succeeded to overload assignment operator for this
int Value();
void setValue(int);
/**
* Construct using an integer value.
*
* #param val: The value to be set.
*/
A_t(int val) : value(val) {}
/**
* int operator.
*
* #return The value stored inside.
*/
operator int() const
{
return value;
}
/**
* int& operator (optional).
*
* #return The variable stored inside.
*/
operator int& ()
{
return value;
}
};
A_t& A_t::operator = (int value)
{
this->setValue(value);
return *this;
}
int A_t::Value() { return this->value; }
void A_t::setValue(int data) { this->value = data; }
int main()
{
A_t object = 3;
int value = object; // Error: cannot convert 'A_t' to 'int' in initialization
cout << value << endl;
return 0;
}

Operator overloading with template question

I'm unable to recognize the following forms of operator overloading, specifically with the template parameter involved. I found this while reading an article on nullptr. I do not see these forms on cppreference overloading page either.
Can anyone explain these forms of overloading and what they are doing?
Thanks
struct nullptr_t
{
void operator&() const = delete; // Can't take address of nullptr
template<class T>
inline operator T*() const { return 0; }
template<class C, class T>
inline operator T C::*() const { return 0; }
};
nullptr_t nullptr;
Starting simpler:
struct A {
operator int() {return 3;}
};
void function() {
A aobject;
int value = aobject; //uses A::operator int()
//value is now 3
}
operator int is a curious member function that allows the struct to be converted to an int. It's very curious in that it's the only case in C++ that uses the return type in order to resolve which overloaded function to call, including that it can resolve template types.
struct A {
operator int*() {return 0;}
};
void function() {
A aobject;
int* value = aobject; //uses A::operator int()
//value now holds the value 0 (NULL)
}
This is the same thing, but now A can be converted to an int*. It is otherwise self explanatory.
struct A {
template<class T>
operator T*() { return 0; }
};
void function() {
A aobject;
short* value = aobject; //uses A::operator T*<int>()
//value now holds the value 0 (NULL)
}
By making A::operator T*() a template method, we can make our class able to be converted to a pointer to any type. This expands the options for what you can convert to. operator T C::*() { return 0; } is similar, but also allows conversion to pointers to any member of any class, which is very rare and advanced.

Understanding C++ choice of conversion operator

I've got an optional-like class (I can't use optional since it's in C++17). It contains a (possible) value along with a flag indicating if it's valid. I've got an explicit bool operator and a conversion operator to get the value out. The problem is, sometimes C++ will choose the bool operator in an explicitly bool context (an if statement), and other times it won't. Can anyone help me understand this behavior:
#include <algorithm>
#include <stdexcept>
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
operator T&&() {
return std::move(value());
}
explicit operator bool() const {
return valid_;
}
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
int main() {
// fine, uses operator bool()
maybe<std::pair<int,int>> m0;
if (m0) {
std::pair<int,int> p = m0;
(void)p;
}
// throws error, uses operator T&&()
maybe<double> m1;
if (m1) {
double p = m1;
(void)p;
}
}
Whenever you write:
if (x)
That is equivalent to having written:
bool __flag(x);
if (__flag)
This is called a contextual conversion to bool (note that it's direct-initialization, so the explicit conversion function is a candidate).
When we do overload resolution on that construction for m0, there's only one valid candidate: explicit operator bool() const.
But when we do overload resolution on that construction for m1, there are two: explicit operator bool() const and operator double&&(), because double is convertible to bool. The latter is a better match because of the extra const qualification on the bool conversion function, even though we have to do an extra double conversion. Hence, it wins.
Would simply remove operator T&&() from your interface, as it doesn't make much sense for this type.
As soon as T is convertible to bool (double is, std::pair is not) your two operators will match and you'll get an ambiguous call, or one may be a better match for some reason.
You should only provide one of the two operators, not both.
Your operator bool and conversion operator are ambiguous in this design.
In the first context, std::pair<int,int> does not cast to bool
so the explicit bool conversion operator is used.
In the second context, double does cast to bool so the T
conversion operator is used, which returns a double, which then
casts to bool implicitly.
Note in the second context, you are calling std::move which puts value in a valid but undefined state, which leads to undefined behavior when you cast value to double a second time in the if block.
I'd use a named member function to indicate if it is valid, and modify the conversion operator:
#include <algorithm>
#include <stdexcept>
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
operator T&&() && { return std::move(value_); } // if rvalue
operator T&() & { return value_; } // if lvalue
operator const T&() const & { return value_; } // if const lvalue
bool valid() const { return valid_; }
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
int main() {
// fine, uses operator bool()
maybe<std::pair<int,int>> m0;
if (m0.valid()) {
std::pair<int,int> p = m0;
(void)p;
}
// throws error, uses operator T&&()
maybe<double> m1;
if (m1.valid()) {
double p = m1;
(void)p;
}
}
EDIT: The conversion operator should only move from member value_ if the maybe object is an rvalue reference. Using && after a functions signature specializes for this case -- please see Kerrek SB's answer for more information.

Template cast operator C++

I am trying to make a little wrapper class such as
template <typename T>
class EdgeTriggeredState
{
public:
void Tick()
{
oldData = newData;
}
EdgeTriggeredState& operator =(const T& v)
{
newData = v;
return *this;
}
// T& operator = (void)
// {
// return oldData;
// }
// T& operator T()
// {
// return oldData;
// }
private:
T oldData;
T newData;
};
Basically I want to be able to directly assign to a variable of type T the value wrapped by the class. I have tried implementing both an assignment (to type T) operator and a cast operator to type T. I am a bit rusty on my C++ as I have been working solely in C. Is there a way to go about implementing this without creating a named getter method?
When I uncomment the first implementation attempt I get error
"../EdgeTriggeredState.h:19:21: error: ‘T& EdgeTriggeredState::operator=()’ must take exactly one argument"
When I uncomment the second implementation (and comment out the first) I get error:
"../EdgeTriggeredState.h:24:16: error: return type specified for ‘operator T’"
When you write an operator T, the return type is implicit, so your code should look something like:
template <typename T>
class DumbWrapper {
T oldData;
T newData;
public:
DumbWrapper& operator = (const T& val) {
newData = val;
return *this;
}
operator T() {
return oldData;
}
};
[Also note the semicolon at the end, and the fact that the constructor and conversion operator were probably intended to be public.]

C++ Templates: Byval/Reference interfering with eachother

Here's a simplified version of my problem. I have a property class. It has data like has_initalized and such which i removed for this example.
When i call a function which uses T its fine. However T& isnt so i decided to write a T& version of it. But this causes all functions which uses plain T to get a compile error. Why is T& interfering with that? For this example how do i get both functions (Q and W) to work without changing main()?
template <class T>
class Property {
T v;
Property(Property&p) { }
public:
Property() {}
T operator=(T src) { v = src; return v; }
operator T() const { return v; }
operator T&() const{ return v; }
T operator->() { return v; }
};
class A{};
void Q(A s){}
void W(A& s){}
int main(){
Property<A> a;
Q(a);
W(a);
}
There is nothing in the overloading rules of C++ which allows the compiler to choose between operatorT() and operatorT&() in the call to Q. So removing the
operator T() const { return v; }
will also remove the ambiguity. But then you'll have a problem because returning a non const reference to a member in a const function is not possible.
For your Q, you can use both conversion functions. You can make the compiler prefer one over the other by making one non-const.
operator T() const { return v; }
operator T&() { return v; }
Now for Q, the operator T& is taken. This way will also fix the call to W to get a non-const reference. You can also return a const reference from the other
operator T const&() const { return v; }
operator T&() { return v; }
This way will still prefer the second conversion function for Q, but if your object a is const and you initialize a const reference, you won't always require to copy v.