2016-12-02 11 views
1

Мы не можем сделать operator++(int)virtual непосредственно из-за проблемы с возвратом. Обычный совет применить любопытное название Curiously Recurring Template Pattern, который я реализовал в меру моих скромных пониманий следующим образом:Можно ли полностью эмулировать виртуальный оператор ++ (int)?

// 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; 
}; 

Много дублирования кода, но, по крайней мере, это позволяет конструкты, как следующее для запуска, который это, безусловно, начало:

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

К сожалению, это не помогает с вещами, как vector<A*>: S и U не имеют общего предка. Если я получаю A из другого базового класса, мне также нужно переместить там шаблонную часть и проблему - ха! - идет рекурсивным.

Итак, любые предложения?

Примечание для редакторов: выучив мой урок, я сохранил оригинал на этот раз. :)

+2

Я не уверен, что полностью понимаю ваш вопрос. Вам нужен базовый класс 'A', который будет иметь операторы' ++ ', но их реализация может быть переопределена в производных классах. Это так? – Angew

+0

@Angew Мне нужен способ (якобы искусственное) ограничение на тип возвращаемой виртуальной функции. Первоначально виртуальные функции не могли возвращать разные типы; затем он был ослаблен ковариантными типами. Лучше, чем ничего, но все еще недостаточно. Приостановление приращения является лишь примером того, когда эта функция становится особенно нелогичной. – sigil

ответ

0

Я не думаю, что это возможно в рамках системы типа C++, вот почему:

Рассмотрим следующий пример: Пусть говорят, что мы каким-то образом добиться этого и есть A* a указатель на базовый класс, от которого U и S был получен. Тогда какой будет тип var = (*a)++;? Это может быть либо U, либо S в зависимости от того, на что указывает a. Но компилятор должен знать тип возврата во время компиляции, потому что оператор ++ (int) возвращает S и U по значению.

Я вижу следующие способы обойти эту проблему, но все они должны изменить типы возвращаемых operator++(int) в иерархии, чтобы сделать их ковариантны (см C++ virtual function return type):

  1. Вернуться указатель на (базовый) класс в иерархии

  2. Если типов есть целые типы (например, оператор ++ вернуться char, int, long для различных классов), то вы можете сделать их все типы возврата ограждающим: long int

  3. Вместо возвращении прямых значений ваших объектов (U или S) возвращает какое-то структуру, которая способна удерживать любой этих типов. (см. http://www.boost.org/doc/libs/1_61_0/doc/html/any.html для возможного общего способа для этого)

+0

'boost :: any' была моей начальной ставкой. Тем не менее, это не позволит мне делать что-либо полезное с сохраненными значениями, а возвращает их только к ** статически ** известным типам.(Или запускайте их через серию операторов 'if-else', сравнивающих строки, которые порождают всю идею.) Если у вас есть пример использования' boost :: any' для моей цели, пожалуйста, поделитесь. – sigil

+0

Ну, посмотрите на изображение высокого уровня: если код не знает _anything_ о сохранении значения, он все равно не сможет его использовать. Поэтому код должен знать _something_ о типах, хранящихся внутри 'any'. Например, он может попытаться проверить, принадлежит ли результат одной или другой иерархии наследования и вызвать соответствующую ему виртуальную функцию, а класс 'any' позволяет реализовать именно это. – Yatima