2012-07-18 26 views
12

Jestem wykonawczych statycznych reguł analizy jakiegoś C++, a jeden z nich zakazuje funkcję powrotu referencję lub wskaźnik do parametru odniesienia funkcji, tj następujące są niezgodne:Czy ta reguła analizy statycznej C++ ma sens?

int *f(int& x) { return &x; } // #1 
const int *g(const int& x) { return &x; } // #2 
int& h(int& x) { return x; } // #3 
const int& m(const int& x) { return x; } // #4 

Podane uzasadnienie jest następujące: "Jest to zachowanie zdefiniowane w ramach implementacji, czy parametr referencyjny jest obiektem tymczasowym, czy odniesieniem do parametru."

Jestem zdziwiony tym, ponieważ operatory strumieniowe w C++ są napisane w ten sposób, np.

std::ostream& operator<<(std::ostream& os, const X& x) { 
    //... 
    return os; 
} 

Sądzę, że jestem przekonany, że operatory strumieniowe w C++ nie wykazują na ogół zachowania zgodnego z implementacją, więc co się dzieje?

Według mojego rozeznania, jak to jest obecnie, spodziewam nr 1 i nr 3 do być dobrze określony, na podstawie tego, że tymczasowych nie może być zobowiązany do odniesienia const, więc int& x odnosi się do rzeczywistych obiektów, które ma żywotność wykraczającą poza zakres funkcji, dlatego zwrot wskaźnika lub odniesienie do tego obiektu jest w porządku. Spodziewałbym się, że # 2 będzie podejrzany, ponieważ tymczasowy może być związany z const int& x, w którym to przypadku próba jego adresu wydaje się złym planem. Nie jestem pewien 4 # - moje przeczucie jest takie, że jest to potencjalnie podejrzane, ale nie jestem pewien. W szczególności, nie jestem jasne, na co by się stało w następującym przypadku:

const int& m(const int& x) { return x; } 
//... 
const int& r = m(23); 
+0

Używasz MSVC++? – Nawaz

+0

@Nawaz: Używam .QL do pisania zapytań na dużych bazach kodu :) Nie sądzę, że powinno to mieć znaczenie, który kompilator, szukam idealnej odpowiedzi niezależnej od platformy. –

+2

Dlaczego zadałem pytanie, ponieważ MSVC++ udostępnia rozszerzenie kompilatora, które pozwala tymczasowym powiązać się z referencjami bez stałych. Jeśli używasz narzędzia do analizy statycznej firmy Microsoft, może ono również uwzględnić to rozszerzenie. – Nawaz

Odpowiedz

8

jak mówisz, nr 1 i nr 3 są w porządku (choć nr 1 jest chyba zły styl).

# 4 jest podejrzany z tego samego powodu # 2; pozwala propagować odniesienie do stałej wartości przez czas jej życia. check

Miejmy:

#include <iostream> 

struct C { 
    C() { std::cout << "C()\n"; } 
    ~C() { std::cout << "~C()\n"; } 
    C(const C &) { std::cout << "C(const C &)\n"; } 
}; 

const C &foo(const C &c) { return c; } 

int main() { 
    const C &c = foo(C()); 
    std::cout << "c in scope\n"; 
} 

This wyjścia:

C() 
~C() 
c in scope 
+0

Dzięki - nie byłem pewien, czy czas trwania tymczasowego przedłużenia zostanie przedłużony przez robienie tego rodzaju rzeczy, czy nie. Wydaje się, że odpowiedź brzmi "nie", ta. –

+0

a co z operatorami strumieni? – Arne

+3

@Arne: Przyjmują i zwracają referencje bez stałych, więc są w porządku. –

1

w C++ 11, # 2 i # 4 mogą być wykonane bezpiecznych jeśli istnieją również rvalue przeciążeń referencyjnych. Tak więc:

const int *get(const int &x) { return &x; } 
const int *get(const int &&x) { return nullptr; } 

void test() { 
    const int x = 0; 
    const int *p1 = get(x); // OK; p1 is &x. 
    const int *p2 = get(x+42); // OK; p2 is nullptr. 
} 

Tak więc, mimo że są podejrzani, mają bezpieczne zastosowania, jeśli programista wie, co robi. Drakońskie byłoby to zabronić.

(Być może bezpieczniej byłoby, gdyby przeciążenie odniesienia rwartacja stała się prywatne, pozostawione niezdefiniowane lub w inny sposób spowodowało błąd czasu kompilacji lub czasu łącza. Jest to szczególnie prawdziwe w przypadku sprawy # 4, w której zwracamy odnośnik ale nie ma nic dobrego do zwrócenia odwołania i język nie zezwala na odniesienie do wartości null.)

+0

Dzięki, zalety, szczególnie na przyszłość. W tym kontekście implementuję dość drakoński zestaw reguł dla naszego komercyjnego narzędzia - są one podpięte standardem, więc muszę uważać na zbyt daleko odbiegające od listu zasad. To powiedziawszy, celem jest zmniejszenie liczby fałszywych alarmów, gdy tylko jest to możliwe. Nie obsługujemy jeszcze C++ 11, ale kiedy to zrobimy, na pewno będę skłonny zmodyfikować regułę, aby wziąć pod uwagę przeciążenia referencyjne rVue. –

+0

Jednym z przypadków, w których zwracanie odniesienia do parametru rvalue jest "bezpieczne", jest użycie 'std :: move' i' std :: forward', które mogą przyjmować argumenty rvalue. IOW, jeśli planujesz przekazać wynik takiej funkcji bezpośrednio do innego wyrażenia, w zależności od tego, która z nich jest (np. Inna funkcja). – Xeo

Powiązane problemy