print(std::cout, point::add(p1, p2));
std::cout << p1 + p2;
#include <iostream>
class point {
public:
point(int x, int y)
: x_{x}
, y_{y} {};
[[nodiscard]] int const x() const {
return this->x_; }
[[nodiscard]] int const y() const {
return this->y_; }
static point add(point const& p1, point const& p2);
private:
int x_;
int y_;
};
void print(std::ostream& os, point const& p) {
os << "(" << p.x() << "," << p.y() << ")";
}
point point::add(point const& p1, point const& p2) {
return point{p1.x() + p2.x(), p1.y() + p2.y()};
}
auto main() -> int {
point p1{1, 2};
point p2{2, 3};
print(std::cout, point::add(p1, p2));
std::cout << "\n";
}
lecture-3/demo361-point1.cpp
Using operator overloading:
#include <iostream>
class point {
public:
point(int x, int y)
: x_{x}
, y_{y} {};
friend point operator+(point const& lhs,
point const& rhs);
friend std::ostream& operator<<(std::ostream& os,
point const& p);
private:
int x_;
int y_;
};
point operator+(point const& lhs, point const& rhs) {
return point(lhs.x_ + rhs.x_, lhs.y_ + rhs.y_);
}
std::ostream& operator<<(std::ostream& os, point const& p) {
os << "(" << p.x_ << "," << p.y_ << ")";
return os;
}
auto main() -> int {
point p1{1, 2};
point p2{2, 3};
std::cout << p1 + p2 << "\n";
}
lecture-3/demo362-point2.cpp
In general we prefer to define friends directly in the class they relate to
Type | Operator(s) | Member / friend |
---|---|---|
I/O | <<, >> | friend |
Arithmetic | +, -, *, / | friend |
Relational, Equality | >, <, >=, <=, ==, != | friend |
Assignment | = | member (non-const) |
Compound assignment | +=, -=, *=, /= | member (non-const) |
Subscript | [] | member (const and non-const) |
Increment/Decrement | ++, -- | member (non-const) |
Arrow, Deference | ->, * | member (const and non-const) |
Call | () | member |
#include <istream>
#include <ostream>
class point {
public:
point(int x, int y)
: x_{x}
, y_{y} {};
friend std::ostream& operator<<(std::ostream& os, const point& type);
friend std::istream& operator>>(std::istream& is, point& type);
private:
int x_;
int y_;
};
std::ostream& operator<<(std::ostream& os, point const& p) {
os << "(" << p.x_ << "," << p.y_ << ")";
return os;
}
std::istream& operator>>(std::istream& is, point& p) {
// To be done in tutorials
}
auto main() -> int {
point p(1, 2);
std::cout << p << '\n';
}
lecture-3/demo363-ui.cpp
class point {
public:
point(int x, int y)
: x_{x}
, y_{y} {};
point& operator+=(point const& p);
point& operator-=(point const& p);
point& operator*=(point const& p);
point& operator/=(point const& p);
point& operator*=(int i);
private:
int x_;
int y_;
};
point& point::operator+=(point const& p) {
x_ += p.x_;
y_ += p.y_;
return *this;
}
point& operator+=(point const& p) { /* what do we put here? */}
point& operator-=(point const& p) { /* what do we put here? */}
point& operator*=(point const& p) { /* what do we put here? */}
point& operator/=(point const& p) { /* what do we put here? */}
point& operator*=(int i) { /* what do we put here? */}
lecture-3/demo354-compassign.cpp
Many operators should be grouped together. This table should help you work out which are the minimal set of operators to overload for any particular operator.
If you overload
operator-(T, U)
Then you should also overload
If you overload
operator+(T, U)
operator+(T)
operator-(T)
operator/(T, U)
operator*(T, U)
operator%(T, U)
operator/(T, U)
operator++()
operator++(int)
operator--()
operator++()
operator--(int)
operator+(T, U)
operator+(U, T)
operator->()
operator*()
operator OP=(T, U)
operator OP(T, U)
#include <iostream>
class point {
public:
point(int x, int y)
: x_{x}
, y_{y} {}
// hidden friend - preferred
friend bool operator==(point const& p1, point const& p2) {
return p1.x_ == p2.x_ and p1.y_ == p2.y_;
// return std::tie(p1.x_, p1.y_) == std::tie(p2.x_, p2.y_);
}
friend bool operator!=(point const& p1, point const& p2) {
return not (p1 == p2);
}
friend bool operator<(point const& p1, point const& p2) {
return p1.x_ < p2.x_ and p1.y_ < p2.y_;
}
friend bool operator>(point const& p1, point const& p2) {
return p2 < p1;
}
friend bool operator<=(point const& p1, point const& p2) {
return not (p2 < p1);
}
friend bool operator>=(point const& p1, point const& p2) {
return not (p1 < p2);
}
private:
int x_;
int y_;
};
auto main() -> int {
auto const p2 = point{1, 2};
auto const p1 = point{1, 2};
std::cout << "p1 == p2 " << (p1 == p2) << '\n';
std::cout << "p1 != p2 " << (p1 != p2) << '\n';
std::cout << "p1 < p2 " << (p1 < p2) << '\n';
std::cout << "p1 > p2 " << (p1 > p2) << '\n';
std::cout << "p1 <= p2 " << (p1 <= p2) << '\n';
std::cout << "p1 >= p2 " << (p1 >= p2) << '\n';
}
lecture-3/demo355-relation1.cpp
#include <istream>
class point {
public:
point(int x, int y)
: x_{x}
, y_{y} {};
point& operator=(point const& p);
private:
int x_;
int y_;
};
point& point::operator=(point const& p) {
x_ = p.x_;
y_ = p.y_;
return *this;
}
lecture-3/demo356-assign.h
#include <cassert>
class point {
public:
point(int x, int y)
: x_{x}
, y_{y} {};
int& operator[](int i) {
assert(i == 0 or i == 1);
return i == 0 ? x_ : y_;
}
int operator[](int i) const {
assert(i == 0 or i == 1);
return i == 0 ? x_ : y_;
}
private:
int x_;
int y_;
};
lecture-3/demo357-subscript.h
lecture-3/demo358-incdec.h
auto main() -> int {
auto rp = RoadPosition(5);
std::cout << rp.km() << '\n';
auto val1 = (rp++).km();
auto val2 = (++rp).km();
std::cout << val1 << '\n';
std::cout << val2 << '\n';
}
// RoadPosition.h:
class RoadPosition {
public:
RoadPosition(int km) : km_from_sydney_(km) {}
RoadPosition& operator++(); // prefix
// This is *always* an int, no
// matter your type.
RoadPosition operator++(int); // postfix
void tick();
int km() { return km_from_sydney_; }
private:
void tick_();
int km_from_sydney_;
};
// RoadPosition.cpp:
#include <iostream>
RoadPosition& RoadPosition::operator++() {
this->tick_();
return *this;
}
RoadPosition RoadPosition::operator++(int) {
RoadPosition rp = *this;
this->tick_();
return rp;
}
void RoadPosition::tick_() {
++(this->km_from_sydney_);
}
lecture-3/demo358-incdec.cpp
#include <iostream>
class stringptr {
public:
explicit stringptr(std::string const& s)
: ptr_{new std::string(s)} {}
~stringptr() {
delete ptr_;
}
std::string* operator->() const {
return ptr_;
}
std::string& operator*() const {
return *ptr_;
}
private:
std::string* ptr_;
};
auto main() -> int {
auto p = stringptr("smart pointer");
std::cout << *p << '\n';
std::cout << p->size() << '\n';
}
lecture-3/demo359-arrow.cpp
#include <vector>
class point {
public:
point(int x, int y)
: x_(x)
, y_(y) {}
explicit operator std::vector<int>() {
std::vector<int> vec;
vec.push_back(x_);
vec.push_back(y_);
return vec;
}
private:
int x_;
int y_;
};
lecture-3/demo360-type.h
#include <iostream>
#include <vector>
int main() {
auto p = point(1, 2);
auto vec = static_cast<std::vector<int>>(p);
std::cout << vec[0] << '\n';
std::cout << vec[1] << '\n';
}
lecture-3/demo360-type.cpp
#include <iostream>
class stringptr {
public:
explicit stringptr(std::string const& s)
: ptr_{new std::string(s)} {}
~stringptr() {
delete ptr_;
}
auto operator->() const -> std::string* {
return ptr_;
}
auto operator*() const -> std::string& {
return *ptr_;
}
private:
std::string* ptr_;
};
auto main() -> int {
auto p = stringptr("smart pointer");
std::cout << *p << '\n';
std::cout << p->size() << '\n';
}
lecture-3/demo361-syntax.cpp
#include <compare>
#include <iostream>
class point {
public:
point(int x, int y)
: x_{x}
, y_{y} {}
// hidden friend - preferred
// return type deduced as std::strong_ordering
friend auto operator<=>(point p1, point p2) = default;
private:
int x_;
int y_;
};
auto main() -> int {
auto const p2 = point{1, 2};
auto const p1 = point{1, 2};
std::cout << "p1 == p2 " << (p1 == p2) << '\n';
std::cout << "p1 != p2 " << (p1 != p2) << '\n';
std::cout << "p1 < p2 " << (p1 < p2) << '\n';
std::cout << "p1 > p2 " << (p1 > p2) << '\n';
std::cout << "p1 <= p2 " << (p1 <= p2) << '\n';
std::cout << "p1 >= p2 " << (p1 >= p2) << '\n';
}
lecture-3/demo355-relation2.cpp
#include <compare>
#include <iostream>
class point {
public:
point(double x, double y)
: x_{x}
, y_{y} {}
// hidden friend - preferred
// return type deduced as std::partial_ordering
friend auto operator<=>(point p1, point p2) = default;
private:
double x_;
double y_;
};
auto main() -> int {
auto const p2 = point{1.0, 2.0};
auto const p1 = point{1.0, 2.0};
std::cout << "p1 == p2 " << (p1 == p2) << '\n';
std::cout << "p1 != p2 " << (p1 != p2) << '\n';
std::cout << "p1 < p2 " << (p1 < p2) << '\n';
std::cout << "p1 > p2 " << (p1 > p2) << '\n';
std::cout << "p1 <= p2 " << (p1 <= p2) << '\n';
std::cout << "p1 >= p2 " << (p1 >= p2) << '\n';
}
// For int-based point
auto const ordering = (p1 <=> p2) == std::strong_ordering::equal;
std::cout << "p1 <=> p2 yields equal " << ordering << '\n';
// For double-based point
auto const ordering = (p1 <=> p2) == std::partial_ordering::equivalent;
std::cout << "p1 <=> p2 yields equivalent " << ordering << '\n';
std::partial_ordering::less
std::partial_ordering::equivalent
std::partial_ordering::greater
std::partial_ordering::unordered
std::weak_ordering::less
std::weak_ordering::equivalent
std::weak_ordering::greater
std::strong_ordering::less
std::strong_ordering::equal
std::strong_ordering::greater
Example types
#include <compare>
#include <compare>
#include <iostream>
class point {
public:
point(int x, int y)
: x_{x}
, y_{y} {}
friend auto operator==(point, point) -> bool = default;
friend auto operator<=>(point const p1, point const p2) -> std::partial_ordering {
auto const x_result = p1.x_ <=> p2.x_;
auto const y_result = p1.y_ <=> p2.y_;
return x_result == y_result ? x_result
: std::partial_ordering::unordered;
}
private:
int x_;
int y_;
};
lecture-3/demo355-relation2.cpp