C++ 重载 [] 与转换

C++ overload[] with transformation

我有一个表示布尔值向量的 C 数据结构;由于我无法控制的原因,布尔值在内部存储为整数,具有两个表示真和假的神奇值(不是 0 和 1 ...)。我已经创建了一个 C++ class 包装这个 C 结构,它工作得很好。我已经将 set()get() 方法实现为:

void Class::set(size_t index , bool value) {
     if (value)
        c_ptr[index] = SPECIAL_TRUE_VALUE;
     else
        c_ptr[index] = SPECIAL_FALSE_VALUE;
}

这没问题;但理想情况下我想重载 operator[],但我不清楚 how/if 我能做到吗 - 由于 bool 和整数值之间的特殊转换?

struct pseudo_reference {
  operator bool()const&&{
    return c->get(index);
  }
  pseudo_reference operator=(bool b)&&{
    c->set(index, b);
    return {c,index};
  }

  // sometimes having named functions is useful:
  bool get() const&& {
    return std::move(*this);
  }
  void set(bool b)&& {
    std::move(*this) = b;
  }
  pseudo_reference()=delete;
private:
  Class* c;
  size_t index;
  pseudo_reference(pseudo_reference&&o)=default; // not exposed
  pseudo_reference(Class* pc, size_t i):c(pc),index(i){}
  friend class Class;
};

Class中:

pseudo_reference operator[](size_t i){
  return {this, i};
}
bool operator[](size_t i)const{
  return c_ptr[index] == SPECIAL_TRUE_VALUE;
}

我存储了一个指针和一个索引,所以我避免在我的 pseudo_reference 中重新实现 get/set 的逻辑。这样的 pseudo_references 可能是短暂的,因此大小优化可能并不重要。

我阻止了所有非右值操作以阻止存储 pseudo_reference。您可以相对无害地对非右值操作进行限制,但根据我的经验,pseudo_references 值的行为类似于引用,因此最好不要持久化它们。

有人仍然可以通过 auto&& x = c[33]; 存储 pseudo_reference,但如果不 move 将无法使用它。希望这能抓住 大多数 容易出错的用法。 auto x = c[33]; 不行。

您可以 return 包装器助手 class 为您处理分配。

struct WrapMe {
    c_ptr_T &value;
    WrapMe(c_ptr_T &_value) : value(_value) {}
    // handles assignment of bool values
    WrapMe & operator=(const bool b) {
       value = (b) ? SPECIAL_TRUE_VALUE : SPECIAL_FALSE_VALUE;
       return *this;
    }
    // handles cast to bool
    operator bool() const { return value == SPECIAL_TRUE_VALUE; }
};

class Class {
   WrapMe operator[](const int idx) { return WrapMe(c_ptr[idx]); }
   // ...
};

要实现 operator[](),您需要 return 一个代理对象,当它出现在 = 的左侧时执行实际分配:

struct proxy {
    proxy& operator=( bool value ) {
        c_.c_ptr[ index_ ] = value ? SPECIAL_TRUE_VALUE : SPECIAL_FALSE_VALUE;
        return *this;
    }
    operator bool() const { // for when it's just used normally, not =
        return c_ptr[ index ] == SPECIAL_TRUE_VALUE;
    }
private:
    Class &c_;
    size_t const index_;
    proxy( Class &c, size_t index ) : c_( c ), index_( index ) { }
    friend class Class;
}

class Class {
public:
    proxy operator[]( size_t index ) {
        return proxy( *this, index );
    }
    bool operator[]( size_t index ) const { // read-only access is easy
        return c_ptr[ index ] == SPECIAL_TRUE_VALUE;
    }
    // ...
};

或者类似的东西。