简体   繁体   中英

Is it possible to fully emulate a virtual operator++(int)?

We cannot make operator++(int) virtual directly because of the return type problem. The usual advice is to apply the curiously named Curiously Recurring Template Pattern , which I implemented to the best of my modest understanding as follows:

// abstract numeric type
template <typename T>
class A {
public:
    virtual T& operator++() = 0;
    virtual T get() const = 0;
    virtual string toString() const = 0;
    virtual T operator++(int) {
        T old(this->get());
        ++*this; // calls operator++() from derived class
        return old;
    }
    friend ostream& operator<<(ostream& os, const A& a) {
        return os << a.toString();
    }
    virtual ~A() = 0;
};

// signed numeric type
class S : public A<S> {
public:
    S(long l) : m_l(l) {}
    virtual S get() const { return m_l; }
    virtual string toString() const { return to_string(m_l); }
    virtual S& operator++() { // no wrapping, caps at LONG_MAX
        if (m_l < LONG_MAX)
            ++m_l;
        return *this;
    }
private:
    long m_l;
};

// unsigned numeric type
class U : public A<U> {
public:
    U(unsigned long ul) : m_ul(ul) {}
    virtual U get() const { return m_ul; }
    virtual string toString() const { return to_string(m_ul); }
    virtual U& operator++() { // no wrapping, caps at ULONG_MAX
        if (m_ul < ULONG_MAX)
            ++m_ul;
        return *this;
    }
private:
    unsigned long m_ul;
};

Lots of code duplication, but at least it allows for constructs like the following to run, which is definitely a start:

template <typename T>
void pinc(A<T>& a) {
    cout << a++ << ' ' << a << endl;
}

int main() {
    S s(LONG_MAX);
    pinc(s);

    U u(LONG_MAX);
    pinc(u);

    return 0;
}

Sadly, it does not help with things like vector<A*> : S and U have no common ancestor. If I derive A from another base class, I also have to move the templated part there and the problem - ha! - goes recursive.

So, any suggestions?

Note to editors: Having learned my lesson, I saved the original this time. :)

I do not think this is possible within C++ type system, here is why:

Consider the following example: Let say we somehow achieve this and have A* a pointer to the base class from which U and S was derived. Then what will be the type of the var = (*a)++; ? It could be either U or S depending on what a is pointing to. But compiler need to know return type during compilation because operator++(int) return S and U by-value.

I see the following ways to work around this problem but they all need to change the return types of operator++(int) in hierarchy to make them covariant (see C++ virtual function return type ):

  1. Return pointer to a (base) class within your hierarchy

  2. If your types is integer types (like operator++ return char , int , long for different classes) then you can make them all return enclosing type: long int

  3. Instead of returning direct values of your objects ( U or S ) return some kind of struct that is capable of holding any of these types. (see http://www.boost.org/doc/libs/1_61_0/doc/html/any.html for possible generic way to do this)

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