2015-05-04 11 views
12

Google C++ Style Guide stati cheModificare il significato del codice mediante sostituzione di dichiarazione anticipata con includono

In casi estremi, la sostituzione di un #include con una dichiarazione anticipata può silenziosamente cambiare il significato del codice.

Potete per favore aiutarmi a trovare alcuni esempi?

+1

Non riesco a pensare a casi in cui questo trasforma un programma valido in un altro programma valido con un significato diverso. Ma può facilmente portare al programma esibire silenziosamente un comportamento indefinito. Per esempio. "** 5.3.5/5 ** Se l'oggetto da eliminare ha un tipo di classe incompleto al punto di cancellazione e la classe completa ha un distruttore non banale o una funzione di deallocazione, il comportamento non è definito." –

+0

@IgorTandetnik Puoi convertire il tuo commento in una risposta, idealmente con un piccolo esempio di codice? – Peter

+0

ogni volta che importa se un tipo è completo o meno fa la differenza. – Walter

risposta

7

Ecco due casi. Uno di loro è UB, l'altro penso che è definito cambiamento di comportamento (supponendo che non ODR o simili violazioni: vale a dire, nessuna chiamata a foo vede mai la definizione di A, ma sono incerto)

namespace N { 
    struct B {}; 
    struct A;//:B{}; 
} 

void foo(N::B*){ 
    std::cout << "B\n"; 
} 
template<class T, class=std::enable_if_t<!std::is_convertible<T*,N::B*>{}>> 
void foo(T*){ 
    std::cout << "T\n"; 
} 

int main() { 
    foo((N::A*)0); 
} 

sostituzione struct A; con struct A:B{}; cambierà quale dei sovraccarichi foo viene chiamato.

Inoltre, delete A; chiamerà ~A() se è visibile quando viene chiamato delete A;. Altrimenti, se c'è un distruttore non banale, abbiamo UB. In questo caso, il significato del codice cambia in quanto va da UB a DB, che suppongo sia un cambiamento di significato.

3

Uno degli esempi più insidiosi di cui sono a conoscenza è il cast in stile C associato all'ereditarietà.

Diciamo che avete:

class Parent1 {}; 
class Parent2 {}; 

class Child : public Parent1, public Parent2 {}; 

Poi in qualche altro file Lanci da parent2 a figlio:

Parent2* parent2_ptr = new Child; 
Child* obj = (Child*)parent2_ptr; 

Con la definizione completa il cast C-stile è un static_cast, correttamente sistemando l'indirizzo. Con una dichiarazione anticipata (di Child) il cast in stile C diventa un reinterpret_cast interrompendo in modo silenzioso il codice.

Problemi correlati