微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么我的右值的重载下标运算符没有被调用 问题所在引用限定符

如何解决为什么我的右值的重载下标运算符没有被调用 问题所在引用限定符

我查看了堆栈溢出并不断找到我认为我已经实现的相同示例。我正在尝试实现一个关联数组。我知道有 std::map,但我想自己实现,以便更好地控制和更好地理解。

我重载了左值和右值的下标运算符。但是在我的代码中只调用了左值的方法,我找不到我错在哪里。任何人都可以指出我正确的方向吗? 这是我的课程代码。现在,它不应该是高效的,只要工作就会让我开心:

template<typename K,typename V>
class AssocArray {

private:
    size_t _arraySize = 0;
    K *_keyArray = nullptr;
    V *_valueArray = nullptr;

    void expandValueArray() {
        auto newValueArray = new V[_arraySize];
        // copy old values
        for (size_t i = 0; i < _arraySize - 1; ++i)
            newValueArray[i] = _valueArray[i];
        if (_valueArray)
            delete[] _valueArray;
        _valueArray = newValueArray;
    }

    void appendToKeyArray(K key) {
        auto newKeyArray = new K[_arraySize];
        // copy old keys
        for (size_t i = 0; i < _arraySize - 1; ++i)
            newKeyArray[i] = _keyArray[i];
        newKeyArray[_arraySize - 1] = key;
        delete[] _keyArray;
        _keyArray = newKeyArray;
    }

    bool keyExists(K key) {
        for (size_t i = 0; i < _arraySize; ++i)
            if (_keyArray[i] == key)
                return true;
        return false;
    }

    size_t getExistingKeyIndex(K key) {
        for (size_t i = 0; i < _arraySize; ++i)
            if (_keyArray[i] == key)
                return i;
    }

public:
    ~AssocArray() {
        delete[] _valueArray;
        delete[] _keyArray;
    }

    V operator[](K key) const {
        if (keyExists(key))
            return _valueArray[getExistingKeyIndex(key)];
        else
            throw std::out_of_range("Key does not exist");
    }

    V &operator[](K key) {
        if (keyExists(key))
            return _valueArray[getExistingKeyIndex(key)];

        // key does not exist
        ++_arraySize;
        appendToKeyArray(key);
        expandValueArray();
        return _valueArray[_arraySize - 1];
    }

    void print() {
        std::cout << "Content of AssocArray:" << std::endl;
        if (!_arraySize) std::cout << "none" << std::endl;
        for (int i = 0; i < _arraySize; ++i) {
            std::cout << "[" << _keyArray[i] << "] => " << _valueArray[i] << std::endl;
        }
    }
};

这是我如何称呼它并产生不良行为的方式:

#include <iostream>
#include "AssocArray.h"

int main() {
       AssocArray<std::string,std::string> assocArray;

        (assocArray)["Toni"] = "seven";
        (assocArray)["Sam"] = "five";
        std::cout << "assocArray before lookup of non existing key:" << std::endl << std::endl;
        assocArray.print();
        // FixMe: appends key to assocArray,but shouldn't
        std::cout << std::endl << "lookup of non existing key:" << std::endl;
        auto key = "Megan";
        std::cout << "[" << key << "] => " << assocArray[key] << std::endl << std::endl;

        std::cout << "assocArray after lookup of non existing key:" << std::endl;
        assocArray.print();
}

输出是这样的(请参阅将 Megan 添加到不应该添加到数组的位置):

assocArray before lookup of non existing key:

Content of AssocArray:
[Toni] => seven
[Sam] => five

lookup of non existing key:
[Megan] => 

assocArray after lookup of non existing key:
Content of AssocArray:
[Toni] => seven
[Sam] => five
[Megan] => 

为什么我没有得到例外?为什么这里调用了左值的重载方法? 感谢您对此进行调查。

解决方法

问题所在

事实证明,你问错了问题。您遇到了逻辑问题,在这种特殊情况下,这与成员函数的 ref-qualification 无关。在您的代码中,我看不到 AssocArray 是右值 (我们要调用 operator[] 的任何用途。

要获得可行的解决方案,我建议您考虑以下两种方法:

  1. 回想一下,当我们下标 std::map 并且还没有这样的键时,std::map 将使用默认值添加这样的键。这正是您当前实现中发生的情况。为了不每次我们想要获取一个元素时都插入一个元素,我们有 at()。所以,你可以添加等效的 at() 成员函数,它可能会抛出或只是默默地返回一些默认值,表示没有这样的元素。
  2. 您可以抛出每个无效的提取。如果下标是在有效键上完成的,则向相应的 &(映射到键的值)返回一个 mapped_type,您可以读取/修改该值。

引用限定符

您应该考虑差异:

V& operator[]();
V operator[]() const;

对比

// An example approach to return types
// You should look mainly on the ref-qualifiers here
V& operator[]() &&; // called on rvalues
const V& operator[]() const&; // called on const and non-const lvalues

在您的类中,您有一个 [] 返回获取的元素的副本,另一个 []const 实例上调用并返回引用。没有运算符可以区分它们是在右值还是左值上调用。

有关引用限定符的更多信息,您可以参考 documentation

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。