2012-07-18 13 views
12

Sto implementando regole di analisi statica qualche C++, e uno di essi vieta una funzione che restituisce un riferimento o puntatore a un parametro di riferimento della funzione, vale a dire i seguenti sono tutti non conforme:Questa regola di analisi statica C++ ha senso così com'è?

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 

L' la giustificazione fornita per questo è che "È un comportamento definito dall'implementazione se il parametro di riferimento è un oggetto temporaneo o un riferimento al parametro."

Sono perplesso, tuttavia, poiché gli operatori di flusso in C++ sono scritti in questo modo, ad es.

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

penso di essere abbastanza sicuro che gli operatori del flusso in C++ non lo fanno in mostra comportamento generale definito dall'implementazione, così che cosa sta succedendo?

Secondo la mia comprensione attuale, mi aspetto che i n. 1 e n. 3 siano ben definiti, in base al fatto che i temporari non possono essere associati a riferimenti non costanti, quindi int& x fa riferimento a un oggetto reale che ha una durata che va oltre lo scopo della funzione, quindi restituire un puntatore o un riferimento a quell'oggetto va bene. Mi aspetterei che il n. 2 sia ingannevole, perché un temporaneo avrebbe potuto essere legato a const int& x, nel qual caso cercare di prendere il suo indirizzo sembrerebbe un pessimo piano. Non sono sicuro del # 4 - il mio istinto è anche potenzialmente dannoso, ma non ne sono sicuro. In particolare, non sono chiare su ciò che sarebbe accaduto nel caso seguente:

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

Stai utilizzando MSVC++? – Nawaz

+0

@Nawaz: Sto usando .QL per scrivere query su basi di codice di grandi dimensioni :) Non penso che dovrebbe importare quale compilatore, sto idealmente cercando una risposta indipendente dalla piattaforma. –

+2

Perché l'ho chiesto perché MSVC++ fornisce l'estensione del compilatore che consente ai vincoli di associare a riferimenti non const. E se stai usando lo strumento di analisi statica di Microsoft, allora potrebbe prendere in considerazione anche questa estensione. – Nawaz

risposta

8

Come dici tu, # 1 e # 3 sono bene (anche se 1 # è senza dubbio un cattivo stile).

# 4 è dubbia per lo stesso motivo # 2; consente di propagare un riferimento const a un passato temporaneo della sua durata. controllo

Let:

#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 uscite:

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

Grazie - Non ero sicuro se la durata del temporaneo sarebbe stata prolungata facendo questo genere di cose o meno. Sembra che la risposta sia un'impresa "no", ta. –

+0

e che dire degli operatori di streaming? – Arne

+3

@Arne: prendono e restituiscono un riferimento non const, quindi stanno bene. –

1

In C++ se ci sono anche sovraccarichi riferimento rvalue sicurezza 11, possono essere realizzati # 2 e # 4. Così:

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. 
} 

Quindi, anche se sono poco raccomandabile, hanno usi sicuri se il programmatore sa cosa stanno facendo. Sarebbe draconiano proibirlo.

(forse più sicuro sarebbe se il sovraccarico di riferimento rvalue const è stato fatto privato, lasciato indefinito, o comunque causato una fase di compilazione o di collegamento in tempo. Questo è particolarmente vero per il caso # 4, in cui si ritorna un riferimento ma non c'è nulla di buono per restituire un riferimento e la lingua non consente riferimenti a null.)

+0

Grazie, buoni punti, soprattutto per il futuro. Il contesto di questo è che sto implementando una regola abbastanza draconiana per il nostro strumento commerciale - sono fissati da uno standard, quindi devo stare attento a deviare troppo lontano dalla lettera delle regole. Detto questo, l'obiettivo è quello di ridurre i falsi positivi laddove possibile. Non supportiamo ancora C++ 11, ma quando lo farei sarei certamente incline a modificare la regola per prendere in considerazione gli overload di riferimento di rvalue. –

+0

Uno dei casi in cui restituire un riferimento a un parametro rvalue è "sicuro" è con 'std :: move' e' std :: forward', che possono entrambi assumere argomenti rvalue. IOW, se hai intenzione di passare il risultato di tale funzione direttamente ad un'altra espressione, quale che sia (come un'altra funzione). – Xeo

Problemi correlati