使用自定义整数 class 作为数组中的索引 (c++)

Use custom integer class as index in an array (c++)

我已经为 c++ 写了 BigInt class,但是我无法使用 class 作为数组的索引。

示例:

int main() {
    int array[5] = {4, 6, 2, 9, 0};
    BigInt index = 1;
    
    std::cout << array[index] << std::endl;
}

这导致:

error: no match for 'operator[]' (operand types are 'int [5]' and 'BigInt')
       cout << array[index] << endl;
                    ^

我尝试实现了以下功能,但无济于事。

int operator[] (BigInt &value) {
    return (int) value;
}

explicit operator int() {
    int result = 0;
    for (auto i : digits)
        result = result * 10 + i;
    
    return result;
}

我确定当 class 本身是一个可迭代对象时会使用 operator[],所以这毫无意义。
explicit operator int() 有效,但仅当我将 BigInt 转换为 int 时,如 array[(int) index]

operator int() 中删除 explicit 效果很好,除了我不能再执行涉及 intBigInt

的操作
// ...

operator int() {
    int result = 0;
    for (auto i : digits)
        result = result * 10 + i;
    
    return result;
}

// ...

int main() {
    int array[5] = {4, 6, 2, 9, 0};
    BigInt index = 1;
    
    std::cout << array[index] << std::endl;   // Works fine
    std::cout << (index == 1) << std::endl;   // Doesn't work
}

以上导致以下错误:

error: ambiguous overload for 'operator==' (operand types are 'BigInt' and 'int')
     cout << (index == 1) << endl;
              ~~~~~ ^~ ~

如何克服这些错误?

谢谢!


编辑:

BigIntclass如下:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

class BigInt {
public:
    std::vector<unsigned short> digits;
    bool negative;

    BigInt(){
        this -> digits = {0};
        this -> negative = false;
    }

    BigInt(const BigInt &other){
        this -> digits = other.digits;
        this -> negative = other.negative;
    }

    BigInt(std::string other) {
        if (other.size() == 0) {
            *this = BigInt();
            return;
        }

        std::reverse(other.begin(), other.end());

        negative = false;
        while (other.back() == '-') {
            negative = !negative;
            other.pop_back();
        }

        digits.clear();
        for (char c : other)
            digits.push_back(c - '0');
        
        (*this).trim();
    }

    BigInt(const char *other) {
        *this = (std::string) other;
    }

    template<typename T>
    BigInt(const T &other) {
         *this = to_std::string(other);
    }

    friend bool operator== (const BigInt &a, const BigInt &b) {
        if (a.negative != b.negative || a.digits.size() != b.digits.size())
            return false;

        for (int i = 0; i < a.digits.size(); i++)
            if (a.digits[i] != b.digits[i])
                return false;

        return true;
    }

    friend bool operator!= (const BigInt &a, const BigInt &b) {
        return !(a == b);
    }

    friend bool operator< (const BigInt &a, const BigInt &b) {
        if (a.negative && b.negative) return (-a) > (-b);
        if (a.negative && !b.negative) return true;
        if (!a.negative && b.negative) return false;

        if (a.digits.size() < b.digits.size()) return true;
        if (a.digits.size() > b.digits.size()) return false;

        for (int i = a.digits.size() - 1; i >= 0; i--) {
            if (a.digits[i] < b.digits[i]) return true;
            if (a.digits[i] > b.digits[i]) return false;
        }

        return false;
    }

    friend bool operator<= (const BigInt &a, const BigInt &b) {
        return !(a > b);
    }

    friend bool operator> (const BigInt &a, const BigInt &b) {
        if (a.negative && b.negative) return (-a) < (-b);
        if (!a.negative && b.negative) return true;
        if (a.negative && !b.negative) return false;

        if (a.digits.size() > b.digits.size()) return true;
        if (a.digits.size() < b.digits.size()) return false;

        for (int i = a.digits.size() - 1; i >= 0; i--) {
            if (a.digits[i] > b.digits[i]) return true;
            if (a.digits[i] < b.digits[i]) return false;
        }

        return false;
    }

    friend bool operator>= (const BigInt &a, const BigInt &b) {
        return !(a < b);
    }

    friend BigInt operator+ (const BigInt &a, const BigInt &b) {
        if (a.negative && b.negative) {
            return -((-a) + (-b));

        } else if (a.negative && !b.negative) {
            return b - (-a);

        } else if (!a.negative && b.negative) {
            return a - (-b);

        } else {
            BigInt result; result.digits.clear();
            int carry = 0;
            
            for (int i = 0; i < std::max(a.digits.size(), b.digits.size()); i++) {
                int sum = carry;
                if (i < a.digits.size()) sum += a.digits[i];
                if (i < b.digits.size()) sum += b.digits[i];

                result.digits.push_back(sum % 10);
                carry = sum / 10;
            }
            
            if (carry)
                result.digits.push_back(carry);
            
            result.trim();
            return result;
        }
    }

    friend BigInt operator+ (const BigInt &value) {
        return value;
    }

    friend BigInt operator- (const BigInt &a, const BigInt &b) {
        if (a.negative && b.negative) {
            return b - a;

        } else if (a.negative && !b.negative) {
            return -((-a) + b);

        } else if (!a.negative && b.negative) {
            return a + (-b);

        } else {
            if (a < b)
                return -(b - a);

            BigInt result; result.digits.clear();
            int carry = 0;

            for (int i = 0; i < a.digits.size(); i++) {
                int dif = a.digits[i] - carry;
                if (i < b.digits.size()) dif -= b.digits[i];

                if (dif < 0) {
                    dif += 10;
                    carry = 1;

                } else {
                    carry = 0;
                }

                result.digits.push_back(dif);
            }

            result.trim();
            return result;
        }
    }

    friend BigInt operator- (const BigInt &value) {
        BigInt result = value;
        result.negative = !result.negative;
        
        result.trim();
        return result;
    }

    friend BigInt operator* (const BigInt &a, const BigInt &b) {
        BigInt result;
        for (int i = 0; i < a.digits.size(); i++) {
            BigInt current; current.digits.clear();
            for (int j = 0; j < i; j++)
                current.digits.push_back(0);
            
            int carry = 0;
            for (int j = 0; j < b.digits.size(); j++) {
                int cur_val = a.digits[i] * b.digits[j] + carry;
                current.digits.push_back(cur_val % 10);
                carry = cur_val / 10;
            }
            
            if (carry)
                current.digits.push_back(carry);

            current.trim();
            result += current;
        }

        result.negative = a.negative ^ b.negative;

        result.trim();
        return result;
    }

    friend BigInt operator/ (const BigInt &a, const BigInt &b) {
        if (b == 0)
            throw std::runtime_error("Division by zero");
        
        BigInt fa = abs(a), fb = abs(b);

        BigInt result, left_over;
        result.negative = a.negative ^ b.negative;

        int index = fa.digits.size() - 1;
        while (true) {
            BigInt cur = left_over; bool first_digit = true;
            while (index >= 0 && cur < fb) {
                cur = cur * 10 + fa.digits[index--];
                if (!first_digit) result *= 10;
                first_digit = false;
            }
            
            int cur_res = 0;
            while (cur >= fb)
                cur -= fb, cur_res++;

            left_over = cur;
            result = result * 10 + cur_res;

            if (index < 0)
                break;
        }

        result.trim();
        return result;
    }

    friend BigInt operator% (const BigInt &a, const BigInt &b) {
        if (b == 0)
            throw std::runtime_error("Division by zero");
        
        BigInt fa = abs(a), fb = abs(b);
        BigInt left_over;

        int index = fa.digits.size() - 1;
        while (true) {
            BigInt cur = left_over;
            while (index >= 0 && cur < fb)
                cur = cur * 10 + fa.digits[index--];
            
            while (cur >= fb)
                cur -= fb;

            left_over = cur;

            if (index < 0)
                break;
        }
    
        left_over.negative = b.negative;
        
        left_over.trim();
        return left_over;
    }

    friend BigInt pow(BigInt a, BigInt b) {
        BigInt result = 1;

        while (b) {
            if (b % 2)
                result = (result * a);

            a *= a;
            b /= 2;
        }

        result.trim();
        return result;
    }

    friend BigInt pow(BigInt a, BigInt b, BigInt mod) {
        BigInt result = 1;

        if (mod == 0)
            throw std::runtime_error("Modulo by zero");
        
        while (b) {
            if (b % 2)
                result = (result * a) % mod;
            
            a = (a * a) % mod;
            b /= 2;
        }

        result.trim();
        return result;
    }

    friend BigInt operator& (BigInt a, BigInt b) {
        BigInt result, power = 1;
        while (a > 0 && b > 0) {
            if (a % 2 && b % 2)
                result += power;
            
            power <<= 1;
            a >>= 1;
            b >>= 1;
        }

        return result;
    }

    friend BigInt operator| (BigInt a, BigInt b) {
        BigInt result, power = 1;
        while (a > 0 || b > 0) {
            if (a % 2 || b % 2)
                result += power;
            
            power <<= 1;
            a >>= 1;
            b >>= 1;
        }

        return result;
    }

    friend BigInt operator^ (BigInt a, BigInt b) {
        BigInt result, power = 1;
        while (a > 0 || b > 0) {
            if (a % 2 != b % 2)
                result += power;
            
            power <<= 1;
            a >>= 1;
            b >>= 1;
        }

        return result;
    }

    friend BigInt operator<< (BigInt a, BigInt b) {return a * pow(2, b);}
    friend BigInt operator>> (BigInt a, BigInt b) {return a / pow(2, b);}

    BigInt operator++ () {return *this = *this + 1;}
    BigInt operator-- () {return *this = *this - 1;}
    BigInt operator++ (int) {BigInt result = *this; *this = *this + 1; return result;}
    BigInt operator-- (int) {BigInt result = *this; *this = *this - 1; return result;}

    BigInt operator+= (BigInt other) {return *(this) = *(this) + other;}
    BigInt operator-= (BigInt other) {return *(this) = *(this) - other;}

    BigInt operator*= (BigInt other){return *(this) = *(this) * other;}
    BigInt operator/= (BigInt other){return *(this) = *(this) / other;}
    BigInt operator%= (BigInt other){return *(this) = *(this) % other;}

    BigInt operator&= (BigInt other){return *(this) = *(this) & other;}
    BigInt operator|= (BigInt other){return *(this) = *(this) | other;}
    BigInt operator^= (BigInt other){return *(this) = *(this) ^ other;}

    BigInt operator<<= (BigInt other){return *(this) = *(this) << other;}
    BigInt operator>>= (BigInt other){return *(this) = *(this) >> other;}

    int operator[] (BigInt &value) {
        return (int) value;
    }

    explicit operator bool() {
        return *this != 0;
    }

    explicit operator int() {
        int result = 0;
        for (auto i : digits)
            result = result * 10 + i;
        
        return result;
    }

    friend bool operator !(const BigInt &other) {
        return other == 0;
    }

    friend std::string to_string(const BigInt &value) {
        std::string result;
        if (value.negative)
            result += '-';

        for (int i = value.digits.size() - 1; i >= 0; i--)
            result += value.digits[i] + '0';

        return result;
    }

    friend BigInt abs(const BigInt &value) {
        BigInt result = value;
        result.negative = false;

        return result;
    }

    void trim() {
        while (digits.size() > 1 && digits.back() == 0)
            digits.pop_back();

        if (digits.size() == 1 && digits.back() == 0)
            negative = false;
    }

    friend std::istream& operator>> (std::istream& in, BigInt &value){
        std::string s; in >> s;
        value = BigInt(s);
        return in;
    }

    friend std::ostream& operator<< (std::ostream& out, const BigInt &value){
        auto digits = value.digits;

        if (value.negative)
            out << "-";
        
        for (auto it = digits.rbegin(); it != digits.rend(); it++)
            out << *it;

        return out;
    }
};

一种可能的方法是仅针对另一个操作数声明标准运算符,该操作数是 class 本身中的 BigInt 对象。

然后你在BigInt中声明一个隐式转换函数成员class。

最后,您声明了自由运算符函数(在 class 之外)以对 BigInt 和 int 进行运算。那些自由函数将具有最高优先级,因为它们的参数将完全匹配并将消除任何不明确的运算符错误。

在您添加 BigInt class 之前,我已经开始编写示例代码,并且我从一个更简单的示例开始:仅正值和仅运算符 + 定义。但是编译和运行时甚至没有警告:

#include <iostream>
#include <vector>
#include <stdexcept>

/* This is a tiny multiprecision class for unsigned values.
 *
 * Currently only addition is implemented, between BigInt objects
 * or between an unsigned value and a BigInt object
 * The class in implicitely convertible to unsigned, but this throws
 * if the value cannot fit into a single unsigned
 */
class BigInt {
    std::vector<unsigned int> val;

public:
    BigInt(unsigned i = 0) {
        val.push_back(i);
    }
    operator unsigned() const {
        if (val.size() > 1) {
            throw std::overflow_error("BigInt is too big");
        }
        return val[0];
    }
    // Implementation of an addition (beware almost untested...)
    BigInt operator+(const BigInt& other) const {
        if (other.val.size() > val.size()) return other + *this;
        BigInt tmp(*this);
        unsigned carry = 0;
        for (int i = 0; i < other.val.size(); i++) {
            tmp.val[i] += carry + other.val[i];
            if (tmp.val[i] < other.val[i] || tmp.val[i] < val[i]) {
                carry = 1;
            }
            else {
                carry = 0;
            }
        }
        for (int i = other.val.size(); i < val.size(); i++) {
            tmp.val[i] += carry;
            if (0 == tmp.val[i]) {
                carry = 1;
            }
            else {
                carry = 0;
                break;
            }
        }
        if (carry) tmp.val.push_back(1);
        return tmp;
    }
    // an auxiliary function to be used in operators
    BigInt add(unsigned i) const {
        // trace to make sure that this method was called...
        std::cout << "add " << this->val[0] << " and " << i << '\n';
        return *this + BigInt(i);
    }
};

// addition operator free functions
BigInt operator+ (int i, const BigInt& b) {
    return b.add(i);
}
BigInt operator+ (const BigInt& b, int i) {
    return b.add(i);              
}

int main() {
    BigInt x(2);
    x = x + 1;
    x = 1 + x;
    std::cout << static_cast<int>( x) << '\n';
    int arr[] = { 1, 2, 3, 4, 5, 6 };
    std::cout << arr[x] << '\n';
}

此代码按预期显示(包括 Clang 11 和 MSVC2022):

add 2 and 1
add 3 and 1
4
5