I have been trying examples from the book Design Patterns in Modern C++: Reusable Approaches for Object-Oriented Software Design by Dmitri Nesteruk and have been trying to compile an example from S.O.L.I.D. Design principles section.
Here is the code:
// This class demonstrates the Open-Closed
// Principle (OCP) in the S.O.L.I.D. Desgin Principle.
// Software entities (classes, modules, functions, etc.)
// should be open for extension, but closed for modification
#include <iostream>
#include <string>
#include <vector>
enum class Color { Red, Green, Blue };
enum class Size { Small, Medium, Large };
struct Product
{
std::string name;
Color color;
Size size;
explicit Product(std::string name, Color color, Size size) : name{name}, color{color}, size{size} { }
};
// (Single-Responsibility Principle) our filtering process
// into two part.
// 1) A filter (a process that takes all items and only returns some)
// 2) A specification (the definition of a predicate to apply to a data element)
// =============== Templates to allow classes to be extended =============== //
template <typename T> struct Specification
{
virtual bool is_satisfied(T* item) = 0;
};
template <typename T> struct Filter {
virtual std::vector<T*> filter(std::vector<T*>&, Specification<T>& ) = 0;
};
template< typename T > struct Display {
virtual void display(const std::vector<T*>&) = 0;
};
// ========== Product Filter ========== //
struct BetterProductFilter : Filter<Product>{
std::vector<Product*> filter(std::vector<Product*>& items, Specification<Product>& spec) override {
std::vector<Product*> result;
for (auto& p : items){
if (spec.is_satisfied(p)){
result.push_back(p);
}
}
return result;
}
};
// ========== Color specification ========== //
struct ColorSpecification : Specification<Product>{
Color color;
ColorSpecification(const Color color) : color{color} {}
bool is_satisfied(Product* item) override { return item->color == color; }
};
// ========== Size specification ========== //
struct SizeSpecification : Specification<Product>{
Size size;
SizeSpecification(const Size size) : size{size} {}
bool is_satisfied(Product* item) override { return item->size == size; }
};
// ========== Product Display ========== ///
struct ProductDisplay : Display<Product>{
void display(const std::vector<Product*> &items){
for(auto &item : items){
std::cout << item->name << std::endl;
}
}
};
// =============== Allowing Composite Specifications =============== //
template <typename T> struct AndSpecification : Specification<T>
{
Specification<T> &first;
Specification<T> &second;
AndSpecification(Specification<T> &first, Specification<T> &second)
: first(first), second(second) {}
bool is_satisfied(T *item) override {
return first.is_satisfied(item) && second.is_satisfied(item);
}
};
// Overloading the && operator for two specifications
template <typename T> AndSpecification<T> operator&&
(Specification<T>& first, Specification<T>& second){
return AndSpecification<T>(first, second);
}
// ======= Rvalue Reference Doesn't work with our class implementation ====== //
template <typename T> AndSpecification<T> operator&&
(Specification<T>&& first, Specification<T>&& second){
return AndSpecification<T>(first, second);
}
int main(){
// Initialization of Products
Product apple{ "Apple", Color::Green, Size::Small };
Product tree{ "Tree", Color::Green, Size::Large };
Product house{ "House", Color::Blue, Size::Large };
// Place all the products into a vector
std::vector<Product*> all {&apple, &tree, &house};
// Filter & Specification Objects
BetterProductFilter filterObj;
// Avoid making extra variables for specifications
//ColorSpecification green(Color::Green);
//SizeSpecification large(Size::Large);
//auto green_and_large = green && large;
auto green_and_large = ColorSpecification(Color::Green) && SizeSpecification(Size::Large);
// Composite Specifications
//AndSpecification<Product> green_and_large{ large, green };
// Product Display Object
ProductDisplay disp;
auto filtered_items = filterObj.filter(all, green_and_large);
disp.display(filtered_items);
return 0;
}
I overloaded the && operator to accepts two references to existing objects and that seems to work fine in this snippet of code:
// Overloading the && operator for two specifications
template <typename T> AndSpecification<T> operator&&
(Specification<T>& first, Specification<T>& second){
return AndSpecification<T>(first, second);
}
In main:
ColorSpecification green(Color::Green);
SizeSpecification large(Size::Large);
auto green_and_large = green && large;
But when I use Rvalue references I end up with a segmentation fault using this code:
// ======= Rvalue Reference Doesn't work with our class implementation ====== //
template <typename T> AndSpecification<T> operator&&
(Specification<T>&& first, Specification<T>&& second){
return AndSpecification<T>(first, second);
}
In main:
auto green_and_large = ColorSpecification(Color::Green) && SizeSpecification(Size::Large);
I'm guessing it is the fact this implementation calls AndSpecification(first, second) and this constructor only handles references to existing objects and not to rvalue references. How could I modify the code above to allow && operator to work with Rvalue references? Thanks!!!
Aucun commentaire:
Enregistrer un commentaire