1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
|
#include <iostream>
#include <math.h>
// need to be compiled with c++17 otherwise the auto will need to be replaced.
struct point{
double x,y;
point(double x_val, double y_val):x(x_val),y(y_val){}
};
template<class T,class X = nullptr_t > //the second parameter is used to specialize the class
class iBox; //forward declaration of the derived template class need here because of the methd "auto scale()"
//this is the base class where all the method can be defined for both versions.
class Box{
public:
virtual point& min_point()=0; //min_point/max_point accessor, =0 means it is virtual and
virtual point& max_point()=0; //needs to be implemented in every derived classes
double area(){return fabs((max_point().x-min_point().x)*(max_point().y-min_point().y));} //one example of method;
auto scale(double f); //this method always returns Box of type &&
};
template<class T> //std::enable ... specilizes the case when the points are passed as references
class iBox<T,typename std::enable_if<std::is_lvalue_reference<T>::value,nullptr_t>::type>
: public Box {
point& _min_point;
point& _max_point;
public:
iBox(point& pmin, point& pmax):_min_point(pmin),_max_point(pmax){}
iBox(point* pmin, point* pmax):_min_point(*pmin),_max_point(*pmax){}
point& min_point(){return _min_point;}
point& max_point(){return _max_point;}
};
template<class T> //std::enable ... specilizes the case when the points are passed as ralue (temporaries here)
class iBox<T,typename std::enable_if<std::is_rvalue_reference<T>::value,nullptr_t>::type>
: public Box{
point _min_point;
point _max_point;
public:
iBox(T&& pmin,T&& pmax ):_min_point(std::move(pmin)),_max_point(std::move(pmax)){}
point& min_point(){return _min_point;} //accessor for p versions
point& max_point(){return _max_point;}
};
template<class T> //factory functions that dispathc the call the right calls, no need wiht c++17
iBox<T&> Make_Box(T& pmin, T& pmax ){return iBox<T&>(pmin,pmax);}
template<class T> //factory functions that dispathc the call the right calls, no need wiht c++17
iBox<T&> Make_Box(T* pmin, T* pmax ){return iBox<T&>(*pmin,*pmax);}
template<class T>
iBox<T&&> Make_Box(T&& pmin,T&& pmax ){ return iBox<T&&>(std::move(pmin),std::move(pmax));}
typedef decltype(Make_Box(point(0,0),point(0,0))) Box_v; //this just give a name to the two types of classes, it is not needed
typedef decltype(Make_Box(std::declval<point&>(),std::declval<point&>())) Box_r;
//this is the definition of the Box::scale, it needs to be located after iBox is completely declared.
auto Box::scale(double f){return Make_Box(point(min_point().x * f, min_point().y * f),point(max_point().x * f,max_point().y * f));}
int main(){
point p1(5,10);
point p2(15,30);
auto b1 = Make_Box(p1,p2);
std::cout << "b1:" << b1.area() << std::endl;
auto b2 = Make_Box(point(5,20),point(15,50));
std::cout << "b2:" << b2.area() << std::endl;
auto b3 = b1.scale(10);
std::cout << "b3:" << b3.area() << std::endl;
auto b4 = b2.scale(10); // scale always return a Box_v
std::cout << "b4:" << b4.area() << std::endl;
Box &b5 = b2; // you cannot create a Box you but can createv a reference for a Box, the correct version of the accessors will be called.
std::cout << "b5:" << b5.area() << std::endl;
Box_v b6(point(5,20),point(15,50));
}
| |