2016-09-14 13 views
10

Podczas odtwarzania przykładami z Modern C++ napisałem poniższy kod.decltype (auto) foo() zwraca lokalny odnośnik bez żadnego ostrzeżenia

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

kod skompilowany z poleceniem:

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

wyjściowa:

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

Jest dla mnie jasne, że decltype(auto) zwraca odniesienie do wyrażenia (ale jeszcze nie intuicyjny), dlatego a i b są nieprawidłowymi odniesieniami (sprawdzonymi przez count==0).

Pytanie brzmi, dlaczego kompilator nie ostrzegł mnie o tym w foo_no_warn?

Czy właśnie znalazłem błąd w kompilatorze, czy to jest jakieś wytłumaczalne zachowanie?

+3

Wydaje się być błędem. clang i gcc do 5 włącznie zgłaszają to ostrzeżenie. – Hayt

+1

@ Powrót AlgirdasPreidžius (c) rzeczywiście zwraca referencję. –

+1

możesz sprawdzić kilka wersji kompilatora dla tego tutaj: http://gcc.godbolt.org/ – Hayt

Odpowiedz

1

Najpierw powiedzmy, że problem nie jest bezpośrednio związany z typem decltype (auto), ponieważ uzyskałby prawie taki sam wynik, gdyby funkcja zwróciła wyraźnie Counter &.

można rozważyć następujący kod:

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 
} 

Choć lokalny odniesienia jest zwracana kompilator nie dać ostrzeżenie ani w VS2015 ani gcc48 (z -Wall). Jednak jeśli usunięto odniesienie z const Typ & danych, kompilator natychmiast złapie problem. Czy uważasz, że takie zachowanie jest błędem? Sporny.

Podstawowym zadaniem kompilatora jest skompilowanie kodu. Ostrzega dewelopera o pewnych oczywistych problemach, ale w większości przypadków nie przeprowadzi żadnej głębszej analizy logiki programu (może ucierpieć czas kompilacji). To jest właśnie narzędzie do analizy statycznej kodu, do którego należy się stosować.

Opisany przypadek można uznać za prosty przykład, ale pojedynczy poziom pośredni wystarcza do "oszukania" kompilatora. Ponieważ w celu zweryfikowania tego kompilator musiałby sprawdzić, co faktycznie jest zwracane z metody getData.

Wykonywanie prostych modyfikacji:

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

stałaby odniesienie zwracane z funkcji returnLocalRef ważnego. Dlatego można to uznać za kompromis kompilatora między złożonością analizy a efektywnością czasu.

Powiązane problemy