Decltype (auto) foo() возвращает локальную ссылку без предупреждения

Во время игры с примерами из Modern С++ я написал следующий код.

#include <string>
#include <iostream>

static int count = 0;

class Counter
{
public:
    Counter() { ++count; };
    Counter(Counter& r) { ++count; };
    Counter(Counter&& r) { ++count; };
    ~Counter() { --count; };

    void foo() {};
};

decltype(auto) foo_warn()
{
    Counter c;
    return (c);             // Warning about returning local reference
}

decltype(auto) foo_no_warn()
{
    Counter c;
    return 1==1 ? c : c;    // No warning, still local reference returned
}

int main()
{
    Counter& a = foo_warn();
    Counter& b = foo_no_warn();

    std::cout << count << std::endl;  // prints 0

    a.foo();
    b.foo();

    return 0;
}

Код, скомпилированный с помощью команды:

g++-6 -std=c++14 -Wall -O0 decl_fail.cpp -o decl_fail

Вывод:

g++-6 -std=c++14 -Wall -O0    decl_fail.cpp   -o decl_fail
decl_fail.cpp: In function ‘decltype(auto) foo_warn()’:
decl_fail.cpp:19:10: warning: reference to local variable ‘a’ returned [-Wreturn-local-addr]
  Counter a;
          ^

Мне ясно, что decltype(auto) возвращает ссылку для выражений (но все же не интуитивно), поэтому a и b являются недопустимыми ссылками (доказано count==0).

Вопрос в том, почему компилятор не предупредил меня об этом в foo_no_warn?

Я только что нашел ошибку в компиляторе, или это объясняет поведение?

Ответ 1

Сначала сообщите, что проблема не связана явно с decltype (auto), так как вы получите почти такой же результат, если функция вернет Counter & в явном виде.

Вы можете рассмотреть следующий код:

typedef std::vector<int> Type;

class DataContainer {
public:
    DataContainer() : data(Type(1024, 0)) {}
    const Type& getData() const { return data; }

private:
    const Type data;
};

const Type& returnLocalRef()
{
    DataContainer container;
    const Type& data = container.getData();
    return data; // o! returning a ref to local - no warning for most compilers
}

Хотя возвращается локальная ссылка, компилятор не дает предупреждения ни в VS2015, ни в gcc48 (с -Wall). Однако, если вы удалили ссылку из const Type & data​​strong > , компилятор сразу поймает проблему. Должны ли вы считать такое поведение ошибкой? Спорно.

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

Описанный случай можно считать простым примером, но одного уровня косвенности достаточно, чтобы "обмануть" компилятор. Поскольку для проверки этого компилятор должен будет проверить, что фактически возвращается из метода getData.

Создание простой модификации:

Type globalData;
...
const Type& getData() const { return globalData; }

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