1

I have written a BigInt class for c++, but I am unable to use the class as an index for arrays.

Example:

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

This results in:

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

I tried implementing the following functions, but to no avail.

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

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

I am sure operator[] is used when the class itself is an iterable, so that's pointless.
And explicit operator int() works, but only when I cast the BigInt as int like array[(int) index]

Removing explicit from operator int() works perfectly, except that I can no longer can perform operations involving both int and BigInt

// ...

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
}

The above results in the following error:

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

How can I overcome these errors?

Thanks!


EDIT:

The BigInt class is as follows:

#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;
    }
};
1
  • Comments are not for extended discussion; this conversation has been moved to chat. Commented Jan 13, 2022 at 3:16

1 Answer 1

2

A possible way would be to declare the standard operators only for the other operand being a BigInt object in the class itself.

Then you declare a implit conversion function member in the BigInt class.

Finally you declare free operator functions (outside of the class) to operate on a BigInt and an int. Those free function will have the highest priority because their argument will exactly match and will get rid of any ambiguous operator error.

I had started to code an example before you have added your BigInt class, and I started with a much simpler one: only positive values and only operator + defined. But is compiles and runs without even a warning:

#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';
}

This code shows as expected (both with Clang 11 and MSVC2022):

add 2 and 1
add 3 and 1
4
5
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.