Безопасно ли возвращать литую ссылку?

Пусть Point - класс, экземпляры которого могут быть явно лишены a wxPoint:

class Point{
private:
    int x;
    int y;
public:
    explicit operator wxPoint() const
    {
        return wxPoint(x, y);
    }

    // More stuff
}

У меня есть функция, которая возвращает ссылку на объект типа Point. Заголовок такой функции:

const Point& GetPoint();

Мой вопрос: безопасно ли определить следующую функцию Foo?

const wxPoint& Foo() const{
    return (wxPoint&) GetPoint();
}

Сначала я реализовал Foo с помощью return (wxPoint) GetPoint();, но создал новый (локальный) объект, и поэтому запускается полезное предупреждение. Показанный здесь код компилируется без предупреждений.

Вся информация, которую я нашел в этом типе кастинга, относится к унаследованным классам, что здесь не так.

Объяснение того, что именно происходит, когда бросать ссылку таким образом, было бы действительно оценено.

Ответ 1

На самом деле ваш оператор преобразования никогда не вызывается. Вы возвращаете ссылку на экземпляр point из GetPoint. Позже вы использовали стиль C, который в вашем случае будет эквивалентен reinterpret_cast<> (см. здесь). Вы ссылаетесь на point на ссылку на wxPoint, и эти два являются полностью несвязанными типами. С другой стороны, любая операция с возвращенной ссылкой - это поведение undefined.

Мое предложение состоит в том, чтобы всегда использовать операторы трансляции С++. У них есть преимущества:

  • Стили стиля С++ проверяются компилятором.
  • Отличия в стиле С++ можно легко найти.
  • Стили стиля С++ выражают намерение программиста.

Ответ 2

Другие ответы уже объяснили, что отбрасывание Point& в wxPoint& просто лежит на компиляторе, сообщая ему, что объект, привязанный к ссылке, является объектом wxPoint, что неверно. Ваша программа будет иметь поведение undefined, если вы попытаетесь получить доступ к объекту wxPoint через эту ссылку, потому что она не привязана к объекту wxPoint. Иногда, когда вы лжете компилятору, он не может дать вам предупреждения и просто должен верить, что вы не делаете что-то безумное.

Исправление состоит в том, чтобы остановить попытку вернуть ссылку на объект, который не существует:

wxPoint Foo() const {
    return GetPoint();
}

Это будет использовать оператор преобразования для построения wxPoint и вернуть его по значению, что в порядке. Попытка вернуться по ссылке, когда нет ссылки на ссылку, не в порядке.

Ответ 3

Отправка ссылки в несвязанный ссылочный тип небезопасна. Хотя сам листинг не имеет побочных эффектов, доступ к исходному объекту посредством ссылки другого типа имеет поведение undefined (если правила псевдонимов типов не являются исключением, как это происходит в случае char, но это не применяется здесь поскольку стандарт не указывает на такое исключение для wxPoint).

Оператор преобразования не участвует и не имеет никакого значения.


Объяснение того, что именно происходит, когда бросать ссылку таким образом, было бы действительно оценено.

"C style" cast делает один из const_cast, static_cast или reinterpret_cast или их комбинацию в этом порядке предпочтения. Нет static_cast, которые применяются к несвязанным ссылкам.

Единственное, что относится к преобразованию const Point& в wxPoint&, - это reinterpret_cast, за которым следует const_cast. reinterpret_cast делает:

6) Выражение lvalue типа T1 может быть преобразовано в ссылку на другой тип T2. Результатом является значение lvalue или xvalue, относящееся к тому же объекту, что и исходное lvalue, но с другим типом. Временное создание не производится, копирование не производится, не создаются конструкторы или функции преобразования. Результирующую ссылку можно получить только безопасно, если это разрешено правилами псевдонимов типа (см. Ниже).


Показанный здесь код компилируется без предупреждений.

Использование reinterpret_cast походит на то, чтобы сообщить компилятору, что вы знаете, что вы делаете, может показаться, что это бредеры, но не беспокоиться, вы знаете, что вы делаете, поэтому не нужно предупреждать об этом. Использование его должно выполняться с большой осторожностью.

Так как приведение в стиле C может делать неинтерактивно reinterpret_cast, то к нему также необходимо проявлять большую осторожность.