2016-05-09 20 views
7

Ho un modello di classe A che contiene un contenitore di puntatori (T*):reinterpret_cast porta a comportamenti non definiti?

template <typename T> 
class A { 
public: 
    // ... 
private: 
    std::vector<T*> data; 
}; 

e un gruppo di funzioni come:

void f(const A<const T>&); 
void g(const A<const T>&); 

E 'OK per chiamare queste funzioni tramite un cast da A<const T> a A<T>?

A<double> a; 
... 
auto& ac = reinterpret_cast<const A<const double>&>(a); 
f(ac); 

Sono abbastanza sicuro che questo codice ha un comportamento non definito.

È pericoloso utilizzare tali conversioni nella vita reale?

+6

Se è UB, è certamente pericoloso utilizzarli nella vita reale. Questo suona sospettosamente come un problema XY, però. – erip

+3

Affidarsi a UB è sempre pericoloso, anche se * "funziona nel mondo reale" *. Basti pensare a progetti come Qt-5, Chromium, KDevelop che sono stati interrotti da gcc6 perché hanno usato alcune sciocchezze 'this == nullptr'. –

+0

@Holt Potrebbe essere un'interfaccia immutabile. – erip

risposta

5

Come A<double> e A<const double> sono tipi non correlati, in realtà non specificato (in origine ho pensato non definito) il comportamento e corrispondentemente sì, è una cattiva idea di utilizzare nella vita reale: Non si sa mai quale sistema (s) o del compilatore (s) si può porta a quello che cambia il comportamento è modi strani.

Riferimento:

5.2.10/11:

Un lvalue di tipo T1 può essere cast nel tipo “riferimento alla T2” se un'espressione di tipo “puntatore a T1” può essere esplicitamente convertito nel tipo "puntatore a T2" usando reinterpret_cast. Quello è un cast di riferimento reinterpret_cast (x) ha lo stesso effetto di la conversione * reinterpret_cast (& x) con gli operatori incorporati e * (e allo stesso modo per reinterpret_cast (x)).

Così ci hanno reindirizzato ad una sezione precedente 5.2.10/7:

Un puntatore oggetto può essere esplicitamente convertito in un puntatore oggetto di un tipo diverso. ... ... Conversione di un valore di tipo "puntatore a T1" al tipo "puntatore a T2" (dove T1 e T2 sono tipi e dove i requisiti di allineamento di T2 non sono più rigidi di quelli di T1) e di nuovo al suo tipo originale restituisce il valore del puntatore originale . Il risultato di qualsiasi altra conversione di questo puntatore non è specificato.

Se f e g sono algoritmi che operano su contenitori, la soluzione più semplice è quella di cambiare loro algoritmi di modello che lavorano su intervalli (coppie iteratore).

+0

Sono d'accordo, ma in questa istanza non sono stato in grado di vedere nulla nello UB di wrt standard. Hai un riferimento? – erip

+4

@erip Si interrompe il rigoroso aliasing, giusto? Quindi, anche se il layout dei due fosse lo stesso, sarebbe comunque UB. – TartanLlama

+3

@MarkB Continuo a pensare che questo sia UB sotto '[basic.lval]/10' (almeno se OP continua per accedere ai parametri). – TartanLlama

4

Anche se lo stesso reinterpret_cast potrebbe essere un comportamento non specificato, il tentativo di accedere ai parametri una volta eseguito il cast è un comportamento non definito.

N3337 [basic.lval]/10: Se un programma tenta di accedere al valore memorizzato di un oggetto attraverso un glvalue di diverso da uno dei seguenti tipi di comportamento è indefinito

- tipo dinamico dell'oggetto,

- una versione cv qualificato di tipo dinamico dell'oggetto,

- un tipo simile (come definito in 4.4) per il tipo dinamico dell'oggetto,

- un tipo che è il segno o tipo senza segno corrispondente al tipo dinamico dell'oggetto,

- un tipo che è la firma o tipo senza segno corrispondente ad una versione cv qualificato di tipo dinamico dell'oggetto ,

- un tipo di aggregato o unione che include uno dei tipi indicati sopra tra i suoi elementi o membri dati statici non (compresi, ricorsivamente, un elemento o componente di dati non statica di una subaggregate o unione contenuto),

- un tipo che è un (possibilmente cv-qualifie d) tipo di classe base del tipo dinamico dell'oggetto,

- un tipo di carattere char o unsigned.

Il tuo esempio è nessuno dei precedenti.

+0

E chiamare qualsiasi funzione membro non statico sulla cosa viola [class.mfct.non-static]/2, anche se non accede ai membri della classe. Praticamente UB tutto intorno se il riferimento risultante viene utilizzato in alcun modo. –

Problemi correlati