We reuse our class SmallInt holding the same range of values as an 8-bit unsigned char— that is, 0 to 255.
If we want to be able to add and subtract SmallInt objects both with other SmallInt objects and with objects of built-in arithmetic type, we must support for these operations by providing six SmallInt operator functions:
class SmallInt {
friend operator+( const SmallInt &, int );
friend operator-( const SmallInt &, int );
friend operator-( int, const SmallInt & );
friend operator+( int, const SmallInt & );
public:
SmallInt( int ival ) : value( ival ) { }
operator+( const SmallInt & );
operator-( const SmallInt & );
// ...
private:
int value;
};
However, it’s a daunting idea! An better alternative is to provide user-defined conversion:
#include <iostream>
class SmallInt {
public:
SmallInt(int ival) : value(ival) {}
operator int(){ return value; }
void showValue()
{
std::cout << "value = "<< value << "\n";
}
private:
int value;
};
int main(){
SmallInt s(1);
s.showValue();
// 1. Convert s to int by conversion operator
// 2. Perform + operator of int
// 3. Convert s+1 to SmallInt by constructor
s = s + 1;
s.showValue();
return 0;
}
What if two classes have different number of members?
#include <iostream>
template<class C>
class ClassA {
public:
// A ctor with two parameters is required!
ClassA(C m, C n) : _m(m), _n(n){}
C get_m(){ return _m; }
C get_n(){ return _n; }
private:
C _m;
C _n;
};
template<class C>
class ClassB {
public:
ClassB(C m) : _m(m){}
template <class D>
// Can be optimized by rvalue ref?
operator ClassA<D>(){
return { static_cast<D>(_m), static_cast<D>(_m+_m) };
}
C get(){ return _m; }
private:
C _m;
};
int main(){
ClassB<double> b(1.6);
ClassA<int> a = b;
std::cout << a.get_m() << "\n"; // 1
std::cout << a.get_n() << "\n"; // 3
return 0;
}
operator in the declaration of a conversion function does not have to be a built-in type name.#include <iostream>
class Unsafe {
private:
int secret = 42;
public:
// Dangerous: returns private data pointer
operator int*() { return &secret; }
// Safe
operator int() { return secret; }
};
int main() {
Unsafe u;
int* p = u; // Calls operator int*()
*p = 99; // Modifies private data!
std::cout << *p << "\n"; // 99
}
SmallInt’s constructor SmallInt(int), defines a set of implicit conversions from values of the constructors’ parameter types to values of type SmallInt. SmallInt(int) , for example, converts values of type int into SmallInt values.explicit.
#include <iostream>
class SmallInt {
public:
SmallInt(int ival) : value(ival) { std::cout << "Calls SmallInt ctor\n"; }
operator int(){ std::cout << "SmallInt ==> int\n"; return value; }
void showValue()
{
std::cout << "value = "<< value << "\n";
}
private:
int value;
};
class Token {
public:
Token(std::string n, int val) : name(n), value(val){}
operator SmallInt(){ std::cout << "Token ==> SmallInt\n"; return value; }
operator int(){ std::cout << "Token ==> int\n"; return value; }
operator std::string(){ std::cout << "Token ==> std::string\n"; return name; }
void showToken()
{
std::cout << "name = " << name << ", value = "<< value << "\n";
}
private:
int value;
std::string name;
};
void Token2Double( double dval) {}
void Token2Int( int ival) {}
void Token2SmallInt( SmallInt s ){}
int main(){
Token t1("weff", 45);
// Token ==> int
// int ==> double (standard conversion)
Token2Double(t1); // fails if operator int() is absent
// Token ==> SmallInt
// Calls SmallInt ctor
Token2SmallInt(t1);
// Token ==> int
Token2Int(t1); // fails if operator int() is absent
// double ==> int (standard conversion)
// Using explicit ctor can avoid the above implicit conversion
// Calls SmallInt ctor
Token2SmallInt(2.1);
// Calls SmallInt ctor
SmallInt s(2);
// SmallInt ==> int
// Calls SmallInt ctor
s = s + 1;
// value = 3
s.showValue();
return 0;
}