Utilizzare -1 come valore di flag per i tipi senza segno (size_t)

Stavo usando -1 come valore di flag per una funzione il cui tipo di ritorno è size_t (un tipo senza segno).

All’inizio non l’ho notato, soprattutto perché non causava alcun errore nel mio codice (lo stavo verificando con x == -1, non x <0).

Ci sono dei sottili motivi per cui non dovrei lasciarlo così com’è? Quando potrebbe comportarsi inaspettatamente? È comunemente usato?

ptrdiff_t è meno comune, richiede più tempo per scrivere e comunque non è proprio il tipo appropriato poiché la funzione restituisce un indice in una matrice.

    -1 si convertirà sempre al valore max senza segno, ciò è dovuto alla sezione 4.7 Conversioni integrali :

    Se il tipo di destinazione non è firmato, il valore risultante è il numero intero senza segno congruente al numero intero sorgente (modulo 2n dove n è il numero di bit utilizzati per rappresentare il tipo senza segno). [Nota: nella rappresentazione a complemento a due, questa conversione è concettuale e non vi è alcun cambiamento nel modello di bit (se non vi è alcun troncamento). -End note]

    La stessa citazione per C99 sarebbe dalla 6.3.1.3 :

    Altrimenti, se il nuovo tipo non è firmato, il valore viene convertito aggiungendo o sottraendo ripetutamente un valore superiore al valore massimo che può essere rappresentato nel nuovo tipo finché il valore non si trova nell’intervallo del nuovo tipo. 49)

    Quindi finiamo con:

     -1 + (UMAX + 1) 

    che è:

     UMAX 

    Il caveat ovvio si trova nel caso di un insieme di elementi con una dimensione uguale alla dimensione maggiore ansible. La possibilità e l’usabilità di ciò che accade nella pratica e in realtà sono la causa del problema a quel punto sono trascurabili.

    Se si guarda la class std::string C ++, si noterà che il membro static std::string::npos è definito esattamente come -1 convertito in std::string::size_type (che in realtà è solo std::size_t Ciò conferisce a questa “tecnica” un senso di precedenza, che le consente di realizzare il Principio di Least Surprise ™, che è sempre una buona cosa.

    Ora, usare -1 direttamente in un confronto come quello sta chiedendo problemi. Dovresti, come nel caso std::string , assicurarti che ci sia un nome accessibile per questo valore che garantirà il suo significato speciale. sfortunatamente, il sistema di tipo C ++ non è abbastanza rigido da impedire a un utente di spararsi nel piede, ma almeno un utente che aderisce alle migliori pratiche documentate non penserà a fare le cose in modo diverso.

    Dopo aver provato a pensare a come questo potrebbe andare storto, mi sono reso conto che c’è il pericolo che la funzione chiamante possa implicitamente trasmettere il valore restituito a un tipo più grande (ad esempio unsigned int a unsigned long long). Quindi verifica se quel valore == -1 sarà falso.

    L’opzione più sicura è utilizzare in modo specifico size_t.max come valore sentinella. Sono sempre a disagio nel cambiare tra tipi firmati e non firmati. A volte penso che l’approccio più ragionevole sia semplicemente rendere tutto firmato (come fa Java).