Вернуть ссылку на это и наследование

Для некоторого синтаксического сахара я хочу вернуть ссылку на this, но при наследовании функция должна возвращать тип дочернего класса:

class base {
  T &operator!() { return *this; }
};
base b; b = !b;

class child : public base {};
child c; c = !c;

Из-за оператора я не могу просто вернуть указатель и выполнить его динамическое приведение, это должна быть ссылка.

Это вообще возможно? Использование decltype(*this) для T не работает, как и auto f()->decltype(*this) из-за this (хотя я не понимаю, почему, в случае с авто)

В Scala вы можете написать что-то вроде:

template<typename T> class base {
  T &f() { return *this; }
};
class child : public base<child> {};

Но мой g++ не примет это (не уверен, что это ошибка или просто нет в спецификации?)

Конечно, есть явный способ, но интересно, можно ли этого избежать, используя функции С++ 11?

class child : public base {
  child &operator!() { base::operator!(); return *this }
};

person pascal    schedule 13.02.2012    source источник
comment
Пожалуйста, не переопределяйте операторы таким образом, чтобы изменить их основное значение. Это действительно плохая идея, которая приведет к путанице и ошибкам.   -  person Rob K    schedule 13.02.2012
comment
в этом случае это DSL, я использую && на самом деле, и хотя он не возвращает логическое значение, семантически это означает и   -  person pascal    schedule 14.02.2012


Ответы (2)


Для этого вы можете использовать CRTP, если вам разрешено создавать base шаблон:

template <typename Derived> class Base {
protected:
    Derived& refToThis() {
        return *static_cast<Derived*>(this);
    }
};

Обратите внимание на дополнительный состав здесь. Причина, по которой это работает, заключается в том, что если у вас есть такой класс:

class Subclass: public Base<Subclass> {
    /* ... */
};

Затем, если вы вызовете refToThis из этого класса, он вызовет версию базового класса. Поскольку класс наследуется от Base<Subclass>, экземпляр шаблона для refToThis будет

    Subclass& refToThis() {
        return *static_cast<Subclass*>(this);
    }

Этот код безопасен, потому что указатель this действительно указывает на объект Subclass. Кроме того, static_cast гарантирует, что приведение не будет выполнено во время компиляции, если производный класс не наследуется от Base должным образом, поскольку тип указателя не будет конвертируемым.

Причина, по которой приведение здесь необходимо, заключается в том, что если вы просто скажете

template <typename Derived> class Base {
protected:
    Derived& refToThis() {
        return *this;
    }
};

Затем в программе возникает ошибка типа, поскольку Base сам по себе не является Derived, и если бы вы могли преобразовать Base& в Derived& без каких-либо проверок, вы могли бы нарушить систему типов.

Тем не менее... Я бы вообще этого не делал. Перегрузка operator! для этой цели делает код менее читаемым, а простое написание *this настолько идиоматично, что его сокрытие сделает ваш код намного труднее для понимания. Использование всего этого механизма шаблонов, чтобы избежать чего-то общего с C++, кажется ошибочным. Если вы делаете что-то еще, прежде чем вернуть ссылку, это нормально, но это не кажется хорошей идеей.

Надеюсь это поможет!

person templatetypedef    schedule 13.02.2012
comment
спасибо, в моем коде было какое-то вмешательство, которое вызвало ошибку в строке class Subclass: public Base<Subclass>, я боялся, что это невозможно в C++. - person pascal; 14.02.2012

Пожалуйста, не делайте этого. В языке уже есть способ сделать это, *whatever_pointer_you_want_to_use_this_crazy_operator_upon. Создание нового способа сделать что-то на языке просто запутает ваших будущих сопровождающих. Есть ли что-то еще, чего вы на самом деле пытаетесь достичь?

person Mark B    schedule 13.02.2012
comment
На самом деле я хочу сделать a << b << c << d, где оператор << определен в базовом классе и наследуется, однако мне нужен подтип с левой стороны, в отличие от ostream - person pascal; 14.02.2012