7. Modèles — Solutions

Chapitre 7 : modèles — Solutions #

Série 7.1 #

Exercice 1 : modèle de fonction exchange #

1242.2_07.01_FunctionTemplateExchange

Time.h

#include <iostream>
#include <iomanip>

constexpr int HOURS_PER_DAY = 24;
constexpr int MINUTES_PER_HOUR = 60;

class Time
{
public:
    Time() = default;
    Time(const Time& other) = default;
    virtual ~Time() = default;
    
    Time(int h, int m);
    Time(double realTime);

    int getHour() const { return m_hour; }
    int getMinute() const { return m_minute; }

    void setHour(int h);
    void setMinute(int m);

    Time& operator=(const Time& other);

    friend Time operator+(const Time& t1, const Time& t2);
    Time operator-(const Time& other) const;

    bool operator==(const Time& other) const { return m_hour == other.m_hour && m_minute == other.m_minute; }
    bool operator<(const Time& other) const { return evaluate() < other.evaluate(); }
    bool operator>=(const Time& other) const { return !(*this < other); }
    bool operator<=(const Time& other) const { return !(other < *this); }
    bool operator!=(const Time& other) const { return !(*this == other); }

    Time& operator++();
    Time operator++(int);

    friend std::istream& operator>>(std::istream& in, Time& t);
    friend std::ostream& operator<<(std::ostream& out, const Time& t);

private:
    int m_hour{12};
    int m_minute{0};

    int evaluate() const;
};

Time.cpp

Time::Time(int h, int m) : m_hour(0), m_minute(0)
{
    setHour(h);
    setMinute(m);
}

Time::Time(double realTime) : m_hour(0), m_minute(0)
{
    if (realTime >= 0 && realTime < HOURS_PER_DAY) {
        setHour(static_cast<int>(realTime));
        setMinute(static_cast<int>(MINUTES_PER_HOUR * (realTime - static_cast<int>(realTime))));
    }
}

void Time::setHour(int h)
{
    if (h >= 0) {
        m_hour = h % HOURS_PER_DAY;
    }
}

void Time::setMinute(int m)
{
    if (m >= 0) {
        m_minute = m % MINUTES_PER_HOUR;
        m_hour += m / MINUTES_PER_HOUR;
        setHour(m_hour);
    } else {
        m_minute = 0;
    }
}

Time& Time::operator=(const Time& other)
{
    if (this != &other) {
        m_hour = other.m_hour;
        m_minute = other.m_minute;
    }
    return *this;
}

Time Time::operator-(const Time& other) const
{
    Time result;
    result.setMinute(m_minute - other.m_minute);
    result.setHour(m_hour - other.m_hour);
    return result;
}

Time operator+(const Time& t1, const Time& t2)
{
    Time result;
    result.setMinute(t1.m_minute + t2.m_minute);
    result.setHour(t1.m_hour + t2.m_hour);
    return result;
}

Time& Time::operator++()
{
    *this = *this + Time(0, 1);
    return *this;
}

Time Time::operator++(int)
{
    Time tmp = *this;
    *this = *this + Time(0, 1);
    return tmp;
}

int Time::evaluate() const
{
    return m_hour * MINUTES_PER_HOUR + m_minute;
}

std::ostream& operator<<(std::ostream& out, const Time& t)
{
    return out << std::setfill('0') << std::setw(2) << t.m_hour
               << ':' << std::setw(2) << t.m_minute;
}

std::istream& operator>>(std::istream& in, Time& t)
{
    char c;
    int hour, minute;

    in >> hour >> c >> minute;
    if (c == ':') {
        t.setMinute(minute);
        t.setHour(hour);
        return in;
    }

    std::println(stderr, "Read error: expected format HH:MM");
    in.setstate(std::ios::failbit);
    return in;
}

main.cpp

template <typename T>
void exchange(T& first, T& second)
{
    T temp = first;
    first = second;
    second = temp;
}

int main()
{
    int n1 = 3, n2 = 5;
    auto f1 = 1.76, f2 = -5.87;
    Time t1(10, 17), t2(11, 12);

    std::println("n1:{} n2:{}", n1, n2);
    exchange(n1, n2);
    std::println("exchange(n1, n2)");
    std::println("n1:{} n2:{}", n1, n2);

    std::println("f1:{} f2:{}", f1, f2);
    exchange(f1, f2);
    std::println("exchange(f1, f2)");
    std::println("f1:{} f2:{}", f1, f2);

    // Time has operator<< but no std::formatter
    std::cout << "t1:" << t1 << " t2:" << t2 << '\n';
    exchange(t1, t2);
    std::println("exchange(t1, t2)");
    std::cout << "t1:" << t1 << " t2:" << t2 << '\n';

    return 0;
}

Exercice 2 : modèle de fonction sum #

1242.2_07.02_FunctionTemplateSum

Time.h

#include <iostream>
#include <iomanip>

constexpr int HOURS_PER_DAY = 24;
constexpr int MINUTES_PER_HOUR = 60;

class Time
{
public:
    Time() = default;
    Time(const Time& other) = default;
    virtual ~Time() = default;
    
    Time(int h, int m);
    Time(double realTime);

    int getHour() const { return m_hour; }
    int getMinute() const { return m_minute; }

    void setHour(int h);
    void setMinute(int m);

    Time& operator=(const Time& other);

    friend Time operator+(const Time& t1, const Time& t2);
    Time operator-(const Time& other) const;
    Time& operator+=(const Time& other);

    bool operator==(const Time& other) const { return m_hour == other.m_hour && m_minute == other.m_minute; }
    bool operator<(const Time& other) const { return evaluate() < other.evaluate(); }
    bool operator>=(const Time& other) const { return !(*this < other); }
    bool operator<=(const Time& other) const { return !(other < *this); }
    bool operator!=(const Time& other) const { return !(*this == other); }

    Time& operator++();
    Time operator++(int);

    friend std::istream& operator>>(std::istream& in, Time& t);
    friend std::ostream& operator<<(std::ostream& out, const Time& t);

private:
    int m_hour{12};
    int m_minute{0};

    int evaluate() const;
};

Time.cpp

Time::Time(int h, int m) : m_hour(0), m_minute(0)
{
    setHour(h);
    setMinute(m);
}

Time::Time(double realTime) : m_hour(0), m_minute(0)
{
    if (realTime >= 0 && realTime < HOURS_PER_DAY) {
        setHour(static_cast<int>(realTime));
        setMinute(static_cast<int>(MINUTES_PER_HOUR * (realTime - static_cast<int>(realTime))));
    }
}

void Time::setHour(int h)
{
    if (h >= 0) {
        m_hour = h % HOURS_PER_DAY;
    }
}

void Time::setMinute(int m)
{
    if (m >= 0) {
        m_minute = m % MINUTES_PER_HOUR;
        m_hour += m / MINUTES_PER_HOUR;
        setHour(m_hour);
    } else {
        m_minute = 0;
    }
}

Time& Time::operator=(const Time& other)
{
    if (this != &other) {
        m_hour = other.m_hour;
        m_minute = other.m_minute;
    }
    return *this;
}

Time Time::operator-(const Time& other) const
{
    Time result;
    result.setMinute(m_minute - other.m_minute);
    result.setHour(m_hour - other.m_hour);
    return result;
}

Time operator+(const Time& t1, const Time& t2)
{
    Time result;
    result.setMinute(t1.m_minute + t2.m_minute);
    result.setHour(t1.m_hour + t2.m_hour);
    return result;
}

Time& Time::operator+=(const Time& other)
{
    *this = *this + other;
    return *this;
}

Time& Time::operator++()
{
    *this = *this + Time(0, 1);
    return *this;
}

Time Time::operator++(int)
{
    Time tmp = *this;
    *this = *this + Time(0, 1);
    return tmp;
}

int Time::evaluate() const
{
    return m_hour * MINUTES_PER_HOUR + m_minute;
}

std::ostream& operator<<(std::ostream& out, const Time& t)
{
    return out << std::setfill('0') << std::setw(2) << t.m_hour
               << ':' << std::setw(2) << t.m_minute;
}

std::istream& operator>>(std::istream& in, Time& t)
{
    char c;
    int hour, minute;

    in >> hour >> c >> minute;
    if (c == ':') {
        t.setMinute(minute);
        t.setHour(hour);
        return in;
    }

    std::println(stderr, "Read error: expected format HH:MM");
    in.setstate(std::ios::failbit);
    return in;
}

main.cpp

template <typename T>
T sum(T tab[], int length)
{
    T result{0};
    for (int i = 0; i < length; ++i) {
        result += tab[i];
    }
    return result;
}

int main()
{
    int intTable[] = {3, 5, 2, 1};
    double doubleTable[] = {2.5, 3.2, 1.8};
    char charTable[] = {'A', '0', 'i', 'o', 'u'};
    Time timeTable[] = {{5, 10}, {3, 22}};

    std::println("{}", sum(intTable, 4));
    std::println("{}", sum(doubleTable, 3));
    // char sum produces an ASCII value (int promotion)
    std::println("{}", sum(charTable, 5));
    // Time has operator<< but no std::formatter
    std::cout << sum(timeTable, 2) << '\n';

    return 0;
}

Série 7.2 #

Exercice 1 : modèle de classe Vector #

1242.2_07.03_ClassTemplateVector

Point.h

#include <iostream>
#include <string>

class Point
{
public:
    Point(double x = 0.0, double y = 0.0, std::string name = "Point");
    Point(const Point& other);
    Point(Point&& other);
    virtual ~Point();

    Point& operator=(const Point& other);

    void show() const;
    void translate(double dx, double dy);
    void translate(const Point& other);

    friend std::ostream& operator<<(std::ostream& os, const Point& p);

    static int counter;

private:
    double m_x{0.0};
    double m_y{0.0};
    std::string m_name;
};

Point.cpp

int Point::counter = 0;

Point::Point(double x, double y, std::string name)
    : m_x(x), m_y(y), m_name(std::move(name))
{
    counter++;
    std::print("[Cstd:{}]", counter);
}

Point::Point(const Point& other)
    : m_x(other.m_x), m_y(other.m_y), m_name(other.m_name)
{
    counter++;
    std::print("[Ccop:{}]", counter);
}

Point::Point(Point&& other)
    : m_x(other.m_x), m_y(other.m_y), m_name(std::move(other.m_name))
{
    counter++;
    std::print("[Cmov:{}]", counter);
}

Point& Point::operator=(const Point& other)
{
    if (this != &other) {
        m_x = other.m_x;
        m_y = other.m_y;
        m_name = other.m_name;
    }
    return *this;
}

Point::~Point()
{
    std::print("[Dstr:{}]", counter);
    counter--;
}

void Point::show() const
{
    std::println("{}: ({}, {})", m_name, m_x, m_y);
}

void Point::translate(double dx, double dy)
{
    m_x += dx;
    m_y += dy;
}

void Point::translate(const Point& other)
{
    translate(other.m_x, other.m_y);
}

std::ostream& operator<<(std::ostream& os, const Point& p)
{
    os << '\n' << p.m_name << ": (" << p.m_x << ", " << p.m_y << ')';
    return os;
}

Vector.h

#include <iostream>
#include <algorithm>

// Class template with type parameter T and non-type parameter dim (default: 3)
template <typename T, int dim = 3>
class Vector
{
public:
  Vector() = default;
  Vector(const T &initElem);
  Vector(const Vector &src);
  virtual ~Vector() = default;

  T &operator[](int index);
  Vector &operator=(const Vector &src);
  Vector &operator=(const T &src);

  // Inline friend to avoid template friendship complexity
  friend std::ostream &operator<<(std::ostream &os, const Vector &v)
  {
    os << "(";
    for (const auto &comp : v.m_data)
    {
      os << " " << comp;
    }
    os << " )";
    return os;
  }

private:
  T m_data[dim];
};

template <typename T, int dim>
Vector<T, dim>::Vector(const T &initElem)
{
  for (auto &comp : m_data)
  {
    comp = initElem;
  }
}

template <typename T, int dim>
Vector<T, dim>::Vector(const Vector &src)
{
  std::copy(std::begin(src.m_data), std::end(src.m_data), std::begin(m_data));
}

// Clamps index to valid range [0, dim-1]
template <typename T, int dim>
T &Vector<T, dim>::operator[](int index)
{
  return m_data[std::clamp(index, 0, dim - 1)];
}

template <typename T, int dim>
Vector<T, dim> &Vector<T, dim>::operator=(const Vector &src)
{
  if (this != &src)
  {
    std::copy(std::begin(src.m_data), std::end(src.m_data), std::begin(m_data));
  }
  return *this;
}

// Fills all components with a single value
template <typename T, int dim>
Vector<T, dim> &Vector<T, dim>::operator=(const T &src)
{
  for (auto &comp : m_data)
  {
    comp = src;
  }
  return *this;
}

main.cpp

int main()
{
    Vector<int, 4> vi = {37};
    vi[3] = 5;
    vi[2] = 2;
    std::cout << vi;

    Vector<double> vd; // 3 elements by default
    vd[0] = 0.0;
    vd[1] = 0.1;
    vd[2] = 0.2;
    std::cout << vd;
    std::println("\nvd[-8] --> out of range: {}", vd[-8]); // clamped to first element
    std::println("\nvd[12] --> out of range: {}", vd[12]); // clamped to last element
    std::println("vd[12] = 99.99");
    vd[12] = 99.99; // clamped to last element
    std::println("vd[2]: {}", vd[2]);

    Vector<double> vd3;
    std::cout << vd;
    vd3 = vd;
    std::cout << vd3;

    Vector<Point, 2> vpt = {Point(0.0, 0.0, "default")};
    Vector<Point, 2> vpt2 = {Point(1.0, 2.0, "other")};
    std::cout << vpt;
    vpt = vpt2;
    std::cout << vpt;

    // Class Template Argument Deduction (C++17)
    // No need to specify template arguments if deducible
    Vector vpt3(vpt2);
    std::cout << vpt3;

    std::println("");

    return 0;
}