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.
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." –
@IgorTandetnik Puoi convertire il tuo commento in una risposta, idealmente con un piccolo esempio di codice? – Peter
ogni volta che importa se un tipo è completo o meno fa la differenza. – Walter