#include "intpair.hpp"
#include "pair.hpp"
#include <iostream>
#include <iomanip>
#include <cassert>
#include <string>
// Note:
// All of our pair classes have the same interface, specifically:
// - A default constructor
// - A copy constructor
// - An assignment operator
// - A constructor that takes two arguments
// - A getFirst() member function
// - A getSecond() member function
// - A setFirst() member function
// - A setSecond() member function
// - A combine() member function
// - A printToStream() member function
// - A non-member operator<<() function
int main() {
// We can make a pair of ints and print it using IntPair.
IntPair ipair0{40, 2};
std::cout << "ipair0: " << ipair0
<< " [requires " << sizeof(ipair0) << " bytes]" << std::endl;
// But we can also make a pair of ints and print it using our Pair class
// template to make an instantiation of our template that holds two ints.
Pair<int, int> pair0{40, 2};
std::cout << "pair0: " << pair0
<< " [requires " << sizeof(pair0) << " bytes]" << std::endl;
// In recent versions of C++, it can deduce the types of the template
// arguments, so we could also write:
Pair apair0{40, 2};
std::cout << "apair0: " << apair0
<< " [requires " << sizeof(apair0) << " bytes]" << std::endl;
// Note that the type of apair0 is still Pair<int, int>, we just didn't
// have to write it out. In CS 70, we prefer not to use this feature
// (which was added in C++ 17) and instead be explicit about the types,
// since it avoids confusion about what's going on.
// When executed, the code above prints:
// ipair0: (40, 2) [requires 8 bytes]
// pair0: (40, 2) [requires 8 bytes]
// apair0: (40, 2) [requires 8 bytes]
// which helps confirm that all three pairs are represented the same way
// in memory. ipair is an IntPair, pair0 is a Pair<int, int>, and apair0
// is also a Pair<int, int> (but we didn't have to write that out).
std::cout << "pair0.combine(): " << pair0.combine() << std::endl;
// Here instead of saying
// Pair<double, double> pair1{3.14, 2.718};
// we'll use our setter member functions to set the values
Pair<double, double> pair1;
pair1.setFirst(3.14);
pair1.setSecond(2.718);
std::cout << "pair1: " << pair1 << std::endl;
std::cout << "pair1.combine(): " << pair1.combine() << std::endl;
// Make a pair with two different kinds of values in it
Pair<float, bool> pair2{2.1, true};
pair2.setFirst(pair2.getFirst() * 2.0);
std::cout << std::boolalpha; // print bools as "true" or "false"
std::cout << "pair2: " << pair2 << std::endl;
std::cout << "pair2.combine(): " << pair2.combine() << std::endl;
// Create a pair of pairs
Pair<Pair<double, double>, Pair<float, bool>> pair3{pair1, pair2};
std::cout << "pair3: " << pair3 << std::endl;
// Make a pair on the heap
Pair<int, int>* pair4ptr = new Pair<int, int>{pair0};
std::cout << "pair4ptr: " << pair4ptr << "; *pair4ptr: " << *pair4ptr
<< std::endl;
// Clean up
delete pair4ptr;
return 0;
}
#ifndef INTPAIR_HPP_INCLUDED
#define INTPAIR_HPP_INCLUDED
#include <iostream>
class IntPair {
public:
IntPair() = default; // Synthesize the default constructor
IntPair(const IntPair&) = default; // Synthesize the copy constructor
IntPair& operator=(const IntPair&) = default; // Synthesize assignment operator
IntPair(int f, int s);
int getFirst() const;
int getSecond() const;
void setFirst(int f);
void setSecond(int s);
int combine() const; // Combine the 1st and 2nd values using the + operator
void printToStream(std::ostream& out) const;
private:
int first_;
int second_;
};
std::ostream& operator<<(std::ostream& out, const IntPair& p);
#endif // INTPAIR_HPP_INCLUDED
#include "intpair.hpp"
#include <iostream>
#include <iomanip>
IntPair::IntPair(int f, int s) :
first_{f},
second_{s} {
// Nothing (else) to do
}
int IntPair::getFirst() const {
return first_;
}
int IntPair::getSecond() const {
return second_;
}
void IntPair::setFirst(int f) {
first_ = f;
}
void IntPair::setSecond(int s) {
second_ = s;
}
int IntPair::combine() const {
return first_ + second_;
}
void IntPair::printToStream(std::ostream& out) const {
out << "(" << first_ << ", " << second_ << ")";
}
std::ostream& operator<<(std::ostream& out, const IntPair& p) {
p.printToStream(out);
return out;
}
#ifndef PAIR_HPP_INCLUDED
#define PAIR_HPP_INCLUDED
#include <iostream>
template <typename F, typename S>
class Pair {
public:
Pair() = default; // Synthesize the default constructor
Pair(const Pair&) = default; // Synthesize the copy constructor
Pair& operator=(const Pair&) = default; // Synthesize assignment operator
Pair(const F& first, const S& second);
const F& getFirst() const;
const S& getSecond() const;
void setFirst(const F& f);
void setSecond(const S& s);
F combine() const; // Combine the 1st and 2nd values using the + operator
void printToStream(std::ostream& out) const;
private:
F first_;
S second_;
};
template <typename F, typename S>
std::ostream& operator<<(std::ostream& out, const Pair<F, S>& p);
#include "pair-private.hpp"
#endif // PAIR_HPP_INCLUDED
// This file implements the class template Pair, whose class definition
// is in pair.hpp. You might expect this file to be called pair.cpp,
// but templates are implemented in header files, not source files,
// because the compiler needs to see the implementation of the template
// in order to generate the code for specific instantiations of the
// template. This is why we have the #include "pair-private.hpp" at
// the end of pair.hpp. (We could have put the implementation of the
// template in pair.hpp, but that file can be seen as specifying the
// interface of the class template, and the implementation is a
// separate concern, so it belongs in a separate file.)
#ifndef PAIR_HPP_INCLUDED
#warning "pair-private.hpp should not be included directly"
#endif
template <typename F, typename S>
Pair<F, S>::Pair(const F& first, const S& second) :
first_{first},
second_{second} {
// Nothing (else) to do
}
template <typename F, typename S>
const F& Pair<F, S>::getFirst() const {
return first_;
}
template <typename F, typename S>
const S& Pair<F, S>::getSecond() const {
return second_;
}
template <typename F, typename S>
void Pair<F, S>::setFirst(const F& f) {
first_ = f;
}
template <typename F, typename S>
void Pair<F, S>::setSecond(const S& s) {
second_ = s;
}
template <typename F, typename S>
F Pair<F, S>::combine() const {
return first_ + second_;
}
template <typename F, typename S>
void Pair<F, S>::printToStream(std::ostream& out) const {
out << "(" << first_ << ", " << second_ << ")";
}
template <typename F, typename S>
std::ostream& operator<<(std::ostream& out, const Pair<F, S>& p) {
p.printToStream(out);
return out;
}