简体   繁体   中英

C++ implicit typecast

I want to convert numerous similar classes into each other. They have at least a common abstract ancestor, which defines 2 basic methods.

I ran into weird typecast errors, so I made a simplified example. On top of the hierarchy: the Integer class. It's an abstract class that have an int val() method. One of it's child is just a holder for a physical int value, while the other references 2 Integers and val() is the sum of its two referenced Integers.

I wrote this code, and I could not figure why the commented expression fails to compile while using a temporary variable works just great.

class Sum;

class Integer {
    public:
        virtual int val(void) const = 0;
        Sum operator+(Integer & other);
};

class Sum : public Integer {
    private:
        Integer &op1, &op2;
    public:
        explicit Sum(Integer &a, Integer &b) : op1(a), op2(b) {};
        int val(void) const {return op1.val() + op2.val();};
};

class Int : public Integer {
    private:
        int v;
    public:
        Int(int value=0) : v(value) {};
        Int(Integer & other) : v(other.val()) {};
        int val() const {return v;};
        Int & operator=(Integer & other){v = other.val(); return *this;};
        Int & operator=(int value){v = value; return *this;};
};

std::ostream & operator<<(std::ostream & out, Integer & i){return out << i.val();}
Sum Integer::operator+(Integer & other){return Sum(*this, other);}

int main(int argc, const char **argv){
    Int a=42, b=57;
//  Int s = a+b; => conversion from ‘Sum’ to non-scalar type ‘Int’ requested
    Sum r = a+b;
    Int s = r;       /* OK */
    cout << a << " + " << b << " = " << s << endl;
    return 0;
}

For functions that take non-const references, like your constructor for Int, you cannot pass temporary objects. A common explanation for this is because if you a function takes a non-const reference, it is allowed to modify the referent, but with a temporary object, this change doesn't really go anywhere since the referent variable isn't accessible outside of the function call.

As DyP suggests in the comments, changing the value to const will provide a solution, or you could simply bind it to a variable like you did with 'Sum r = a+b'.

class Int : public Integer {
    private:
        int v;
    public:
        Int(int value=0) : v(value) {};
        Int(Integer & other) : v(other.val()) {};
        int val() const {return v;};
        Int & operator=(Integer & other){v = other.val(); return *this;};
        Int & operator=(int value){v = value; return *this;};
};

The constructor Int(Integer & other) doesn't modify its argument, so could (should) make that reference const :

Int(Integer const& other) : v(other.val()) {};

This also solves your problem:

Sum Integer::operator+(Integer & other);
Int s = a+b;

The operator + (which should arguably be a free function instead of a member function) returns a prvalue/temporary of type Sum . This temporary cannot bind to a non-const lvalue reference, therefore the constructor Int(Integer & other) cannot be used.

Similarly for Int & operator=(Integer & other) , a const reference is sufficient.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM