Почему компиляторы выдают предупреждение о возврате ссылки на локальную переменную стека, если это поведение undefined?

Стандарт C ++ утверждает, что возврат ссылки на локальную переменную (в стеке) является неопределенным поведением, так почему же многие (если не все) текущие компиляторы только выдают предупреждение для

struct A{
};

A& foo()
{
    A a;
    return a; //gcc and VS2008 both give this a warning, but not a compiler error
}

Разве не лучше, если бы компиляторы выдавали ошибку вместо предупреждения для этого кода?

Есть ли какие-то большие преимущества в том, чтобы позволить этому коду компилироваться только с предупреждением?

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

26
задан Douglas Leeder 19 July 2011 в 12:49
поделиться

5 ответов

Практически невозможно проверить с точки зрения компилятора, возвращаете ли вы ссылку на временный объект. Если бы стандарт предписывал диагностировать ошибку, написание компилятора было бы практически невозможным. Рассмотрим:

bool not_so_random() { return true; }
int& foo( int x ) {
   static int s = 10;
   int *p = &s;
   if ( !not_so_random() ) {
      p = &x;
   }
   return *p;
}

Приведенная выше программа корректна и безопасна для запуска, в нашей текущей реализации гарантируется, что foo вернет ссылку на переменную static, что безопасно. Но с точки зрения компилятора (и с отдельной компиляцией на месте, где реализация not_so_random() недоступна, компилятор не может знать, что программа правильно сформирована.

Это игрушечный пример, но вы Можно представить похожий код с разными путями возврата, где p может ссылаться на разные долгоживущие объекты во всех путях, которые возвращают *p.

23
ответ дан 28 November 2019 в 07:13
поделиться

Неопределенное поведение не является ошибкой компиляции, это просто не правильно сформированная программа C ++. Не каждая плохо сформированная программа несовместима, просто она предсказуема . Держу пари, что в принципе даже компьютер не может решить, является ли данный текст программы корректной программой C ++.

Вы всегда можете добавить -Werror в gcc, чтобы предупреждения прекращали компиляцию с ошибкой!

Чтобы добавить еще одну любимую тему SO: Вы хотите, чтобы ++i++ также вызывал ошибку компиляции?

10
ответ дан 28 November 2019 в 07:13
поделиться

Если вы возвращаете указатель / ссылку на локальную внутреннюю функцию, поведение хорошо определено , если вы не разыменовываете указатель / ссылку, возвращенную из функции.

Это неопределенное поведение, только когда кто-то отменяет возвращаемый указатель.

Является ли это неопределенным поведением или нет, зависит от кода, вызывающего функцию, а не от самой функции.

Таким образом, просто во время компиляции функции компилятор не может определить, является ли поведение Undefined или Well Defined. Лучшее, что он может сделать, - это предупредить вас о потенциальной проблеме, и это так!

Пример кода:

#include <iostream>

struct A
{ 
   int m_i;
   A():m_i(10)
   {

   } 
};  
A& foo() 
{     
    A a;
    a.m_i = 20;     
    return a; 
} 

int main()
{
   foo(); //This is not an Undefined Behavior, return value was never used.

   A ref = foo(); //Still not an Undefined Behavior, return value not yet used.

   std::cout<<ref.m_i; //Undefined Behavior, returned value is used.

   return 0;
}

Ссылка на стандарт C ++:
раздел 3.8

До того, как началось время жизни объекта, но после того, как хранилище, которое будет занимать объект, было выделено 34) или, после окончания срока службы объекта и до повторного использования или освобождения хранилища, в котором занятый объект, может использоваться любой указатель, который ссылается на место хранения, где объект будет или был расположен, но только ограниченным образом. Такой указатель относится к выделенному хранилищу (3.7.3.2), и использование указателя, как если бы указатель имел значение type void*, является четко определенным. Такой указатель может быть разыменован, но результирующее значение l может использоваться только ограниченным образом , как описано ниже. Если объект будет или имел тип класса с нетривиальным деструктором, и указатель используется в качестве операнда выражения удаления, программа имеет неопределенное поведение. Если объект будет или имел тип класса не POD, программа имеет неопределенное поведение, если:

- .......

8
ответ дан 28 November 2019 в 07:13
поделиться

Потому что стандарт не ограничивает нас.

Если вы хотите стрелять себе в ноги, вы можете сделать это!

Однако давайте посмотрим и пример, где это может быть полезно:

int &foo()
{
    int y;
}

bool stack_grows_forward()
{
    int &p=foo();
    int my_p;
    return &my_p < &p;
}
3
ответ дан 28 November 2019 в 07:13
поделиться

Это очень плохая практика - полагаться на это, но я верю, что во многих случаях (и это никогда не является хорошей ставкой), эта ссылка на память все равно будет действительной, если между временем не будет вызвано ни одной функции foo() возвращается и время, когда вызывающая функция использует возвращаемое значение. В этом случае эта область стека не будет иметь возможности перезаписаться.

В C и C ++ вы можете выбрать доступ к произвольным разделам памяти в любом случае (конечно же, в пределах пространства памяти процесса) с помощью арифметики указателей, так почему бы не разрешить возможность создания ссылки на то место, где вы захотите?

0
ответ дан 28 November 2019 в 07:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: