c++ 关于operator []的重载函数的调用顺序

rxztt3cl  于 2023-04-01  发布在  其他
关注(0)|答案(2)|浏览(146)

我正在创建自己的边界检查数组类,但遇到了一个问题,即当使用运算符[]时,它无法调用适当的重载函数。
第一个重载运算符[]用于获取数组示例的实际值(=content):这样指令num1 = list1[2]是可能的。
第二个重载运算符[]用于将给定的右值赋给MyArrayList
但在运行main函数时,无法调用第二个,整个代码如下:

#include <iostream>

template <typename Data>
class MyArrayList {
    private :
        Data* data_storage; 
        int num_of_elements;
        int array_size;
        int idx_cursor;
    public :
        MyArrayList(int _size);
        void add_element(Data new_data);
        void remove_element();
        Data operator[](int idx) const;
        MyArrayList& operator[](int idx);
        void operator=(Data new_data);
        ~MyArrayList();
};

template <typename Data>
MyArrayList<Data>::MyArrayList(int _size) {
    array_size = _size;
    data_storage = new Data[array_size];
    num_of_elements = 0;
    idx_cursor = 0;
}

template <typename Data>
void MyArrayList<Data>::add_element(Data new_data) {
    if(num_of_elements > array_size) {
        std::cout << "Unable to store more." << std::endl;
        return; 
    }
    data_storage[++num_of_elements - 1] = new_data;
}

template <typename Data>
void MyArrayList<Data>::remove_element() {
    if(num_of_elements <= 0) {
        std::cout << "Unable to delete anymore" << std::endl; 
        return;
    }
    
    Data* temp = data_storage;
    delete[] data_storage;
    
    data_storage = new Data[--num_of_elements];  
    for(int i = 0; i < num_of_elements; i++)
        data_storage[i] = temp[i];
    
    delete[] temp; 
}

template <typename Data>
MyArrayList<Data>::~MyArrayList() {
    delete[] data_storage;
}

template <typename Data>
Data MyArrayList<Data>::operator[](int idx) const { //var = list1[5];
    if(idx < 0) {
        int new_idx = idx;
        while(new_idx < 0) {
        std::cout << "IndexOutofBounds! Enter new index." << std::endl;
        std::cin >> new_idx; std::cout << std::endl;
        new_idx--;
        }
        idx = new_idx;
    }
    
    return data_storage[idx];
}

template <typename Data>
 MyArrayList<Data>& MyArrayList<Data>::operator[](int idx){ // list1[2] = 5;
    idx_cursor = idx;
    return *this;

}

template <typename Data>
void MyArrayList<Data>::operator=(Data new_data){
    data_storage[idx_cursor] = new_data;        
} 

int main() {
    int num1;
    MyArrayList<int> list1(5);
    
    list1.add_element(6);
    list1.add_element(7);
    list1.add_element(8);
    list1.add_element(9);
    list1[2] = 5; //possible
    //std::cout << num1 << std::endl; //not possible. probably not calling the first overloaded operator[]()? 
    
}

我第一次尝试使用friend关键字重写第二个重载的operator[](),但在第二次猜测中,我认为这不是一个好主意,并且没有办法解决这个问题。

ogq8wdun

ogq8wdun1#

可以调用它,但是你必须告诉编译器你希望你的对象是const

std::cout << const_cast<const MyArrayList<int>&>(list1)[2] << std::endl;

但是你的方法会给你带来一些非常愤怒的用户。你打破了用户对你的类的每一个合理的期望。你的赋值操作符不返回赋值对象,它允许从包含的类型赋值(并且不允许赋值实际不同的容器),并且它依赖于内部状态,出于某种原因,通过operator[]设置。
预期的实现应如下所示:

const Data& MyArrayList<Data>::operator[](int idx) const 
{
    //implement actual accessing here
}

Data& MyArrayList<Data>::operator[](int idx)
{
    // looks scary, but it's valid way to avoid code duplication
    // We first cast it to const to call const version of operator, then we remove const from the result
    // It's safe because we know this isn't const, so its members also aren't const
    return const_cast<Data&>(const_cast<const MyArrayList<Data>*>(this)->operator[](idx));

    //it's not impossible to just copy implementation from const operator here, but one should avoid duplicating code
}

// copy assignment operator. Don't forget the other functions required for the Rule of Five!
MyArrayList<Data>& MyArrayList<Data>::operator=(const MyArrayList<Data>&)
{
    // implement copying from another object here
    return *this;
}
8yoxcaq7

8yoxcaq72#

这里的基本问题是,您不理解当类MyArrayList重载了operator[](),并且提供了const和非const版本时会发生什么。(在这种情况下,类被模板化的事实并没有改变什么)。

Data operator[](int idx) const;
MyArrayList& operator[](int idx);

**注意:**我并没有提到你的const和非const重载有不同的返回类型(对于MyArrayList<int>operator[]()const重载返回int,而非const重载返回MyArrayList<int> &)。简而言之-不要这样做!

在调用站点,给定类型为MyArrayList<int>的对象list1和表达式list1[2],两个operator[]()都是被调用的候选对象。
const和非const重载都可用时,调用哪个(粗略地说)取决于对象list1是否为const
在你的代码中,list1不是const。这就是为什么main()可以调用list1.add_element(6)(以及add_element()的其他调用)。这也是为什么operator[]()的非const重载被调用。
在表达式list1[2] = 5中,list1[2]作为一个完整的子表达式进行计算。= 5与调用operator[]()的哪个重载无关,当然也不会强制选择operator[]()(您的const重载返回int &)。
粗略地说,operator[]()const版本将被调用的唯一情况是对象是const或者对象是通过const引用访问的。
例如,如果将以下两行添加到main()

const MyArrayList<int> &ref(list1);
ref[2];

operator()[]const过载将被调用。
另一个例子是创建一个函数

void func(const MyArrayList<int> &object)
{
   object[2];
}

这也将调用相同的const过载。

相关问题