右值引用重载 && 运算符

Posted

技术标签:

【中文标题】右值引用重载 && 运算符【英文标题】:Rvalue reference overloading && operator 【发布时间】:2021-04-23 12:51:40 【问题描述】:

我一直在尝试 Dmitri Nesteruk 所著的Design Patterns in Modern C++: Reusable Approaches for Object-Oriented Software Design一书中的示例,并一直在尝试从 S.O.L.I.D. 中编译一个示例。设计原则部分。

代码如下:

// 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) : namename, colorcolor, sizesize    
;

// (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) : colorcolor 

  bool is_satisfied(Product* item) override  return item->color == color; 
;

// ========== Size specification ========== //

struct SizeSpecification : Specification<Product>

  Size size;

  SizeSpecification(const Size size) : sizesize 

  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;

我重载了 && 运算符以接受对现有对象的两个引用,并且在这段代码的 sn-p 中似乎可以正常工作:

// Overloading the && operator for two specifications
template <typename T> AndSpecification<T> operator&&
  (Specification<T>& first, Specification<T>& second)
  return AndSpecification<T>(first, second);

主要:

ColorSpecification green(Color::Green);
SizeSpecification large(Size::Large);
auto green_and_large = green && large;

但是当我使用右值引用时,我最终会使用以下代码出现分段错误:

// ======= 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);
    

主要:

auto green_and_large = ColorSpecification(Color::Green) && SizeSpecification(Size::Large);

我猜这是这个实现调用 AndSpecification(first, second) 的事实,而这个构造函数只处理对现有对象的引用而不是右值引用。如何修改上面的代码以允许 && 运算符使用右值引用?谢谢!!!

【问题讨论】:

由于AndSpecification 存储引用,因此您需要确保它引用的对象的寿命比它长。 (引用成员的使用让我怀疑这本书是否真的好。) 【参考方案1】:

你的结论是正确的。

如果你只是想获得一个程序。

// it is your ColorSpecification class with copy constructor addition
struct ProductColorSpecification final : Specification<Product> 
    Color color;
    ProductColorSpecification(const Color color)
    : Specification<Product>()
    , colorcolor
    
    ProductColorSpecification(const ProductColorSpecification& o)
    : Specification<Product>()
    , coloro.color
    
    bool is_satisfied(Product* item) const override  return item->color == color; 
;

// it is your SizeSpecification class with copy constructor addition
struct ProductSizeSpecification final : Specification<Product> 
    Size size;
    ProductSizeSpecification(const Size size)
    : Specification<Product>()
    , sizesize 
    ProductSizeSpecification(const ProductSizeSpecification& o)
    : Specification<Product>()
    , sizeo.size
    
    bool is_satisfied(Product* item) const override  return item->size == size; 
;

struct ProductAndSpecification final : Specification<Product> 
    ProductColorSpecification *color_spec;
    ProductSizeSpecification  *size_spec ;
    ProductAndSpecification(const Specification<Product>& first_spec, const Specification<Product>& second_spec)
    : Specification<Product>()
    , color_specnullptr
    , size_specnullptr
    
        const auto* spec_f_c = dynamic_cast<const ProductColorSpecification*>(&first_spec );
        const auto* spec_f_s = dynamic_cast<const ProductSizeSpecification *>(&first_spec );
        const auto* spec_s_c = dynamic_cast<const ProductColorSpecification*>(&second_spec);
        const auto* spec_s_s = dynamic_cast<const ProductSizeSpecification *>(&second_spec);
        if (spec_f_c && spec_s_s) 
            color_spec = new ProductColorSpecification(*spec_f_c);
            size_spec  = new ProductSizeSpecification (*spec_s_s);
         else if (spec_f_s && spec_s_c) 
            color_spec = new ProductColorSpecification(*spec_s_c);
            size_spec  = new ProductSizeSpecification (*spec_f_s);
         else 
            //  bad specs
        
    
    ~ProductAndSpecification()
    
        if (color_spec)
            delete color_spec;
        if (size_spec)
            delete size_spec;
    

    friend ProductAndSpecification operator&&(Specification<Product>&& first, Specification<Product>&& second);

    bool is_satisfied(Product *item) const override 
        if (color_spec && size_spec)
            return color_spec->is_satisfied(item) && size_spec->is_satisfied(item);
        else if (color_spec)
            return color_spec->is_satisfied(item);
        else if (size_spec)
            return size_spec->is_satisfied(item);
        else
            return false;
    
;

struct ProductAndSpecification operator&&(Specification<Product>&& first, Specification<Product>&& second) 
    return ProductAndSpecification(first, second);

但它仍然是糟糕的设计(我想你明白为什么了)。

PS:: 不要忘记在 main 函数中更改规范对象。

【讨论】:

我试过了,但是我的 Specification 结构没有我想要的构造函数,因为它是一个抽象类,所以我无法创建该类型的对象。 你是对的,答案已被编辑。存在堆分配,因为您需要在 ProductAndSpecification 类中使用默认的颜色和大小定义,否则,

以上是关于右值引用重载 && 运算符的主要内容,如果未能解决你的问题,请参考以下文章

何时重载按引用传递(左值和右值)优于按值传递?

重新理解C11的右值引用

C11新特性右值引用&&

是否有任何理由使用右值引用重载运算符?

C++左值引用和右值引用

左值右值右值引用与move()forward()