int numeral -> regole di conversione del puntatore


19

Considera il seguente codice.

void f(double p) {}
void f(double* p) {}

int main()
{ f(1-1); return 0; }

MSVC 2017 non lo compila. Immagina che ci sia un'ambigua chiamata sovraccaricata, così come 1-1è 0e può essere convertita in double*. Anche altri trucchi, come 0x0, 0Lo static_cast<int>(0), non funzionano. Anche la dichiarazione di a const int Zero = 0e la chiamata f(Zero)producono lo stesso errore. Funziona correttamente solo se Zeronon lo è const.

Sembra che lo stesso problema si applichi a GCC 5 e precedenti, ma non a GCC 6. Sono curioso di sapere se questa è una parte dello standard C ++, un bug MSVC noto o un'impostazione nel compilatore. Un Google superficiale non ha prodotto risultati.

Risposte:


18

MSVC considera 1-1una costante puntatore null. Ciò era corretto dallo standard per C ++ 03, in cui tutte le espressioni di costante integrale con valore 0erano costanti di puntatore null, ma è stata modificata in modo tale che solo i valori letterali di zero interi sono costanti di puntatore null per C ++ 11 con CWG numero 903 . Questo è un cambiamento sostanziale , come puoi vedere nel tuo esempio e come è anche documentato nello standard, vedi [diff.cpp03.conv] dello standard C ++ 14 (bozza N4140).

MSVC applica questa modifica solo in modalità conformità. Quindi il tuo codice verrà compilato con il /permissive-flag, ma penso che il cambiamento sia stato implementato solo in MSVC 2019, vedi qui .

Nel caso di GCC, GCC 5 passa automaticamente alla modalità C ++ 98, mentre GCC 6 e versioni successive passano alla modalità C ++ 14, motivo per cui il cambio di comportamento sembra dipendere dalla versione di GCC.

Se si chiama fcon una costante puntatore null come argomento, la chiamata è ambigua, poiché la costante puntatore null può essere convertita in un valore puntatore null di qualsiasi tipo di puntatore e questa conversione ha lo stesso rango della conversione di int(o di qualsiasi tipo integrale) a double.


-1

Il compilatore funziona correttamente, in conformità con [over.match] e [conv] , più precisamente [conv.fpint] e [conv.ptr].

Una sequenza di conversione standard è [blah blah] Zero o una [...] conversione fluttuante, conversione puntatore, [...].

e

Un valore di un tipo intero o di un tipo di enumerazione senza ambito può essere convertito in un valore di tipo a virgola mobile. Il risultato è esatto se possibile [blah blah]

e

Una costante puntatore null è un numero intero letterale con valore zero o [...]. Una costante di puntatore null può essere convertita in un tipo di puntatore; il risultato è il valore del puntatore null di quel tipo [blah blah]

Ora, la risoluzione del sovraccarico è quella di scegliere la migliore corrispondenza tra tutte le funzioni candidate (che, come caratteristica divertente, non devono nemmeno essere accessibili nella posizione della chiamata!). La migliore corrispondenza è quella con parametri esatti o, in alternativa, il minor numero di conversioni possibili. È possibile che si verifichino zero o una conversione standard (... per ogni parametro) e zero è "migliore" di una.

(1-1)è un valore intero letterale con valore 0.

Puoi convertire il numero intero zero letterale in ciascuno di uno doubleo double*(o nullptr_t), con esattamente una conversione. Quindi, supponendo che sia dichiarata più di una di queste funzioni (come nel caso dell'esempio), esiste più di un singolo candidato e tutti i candidati sono ugualmente bravi, non esiste corrispondenza migliore. È ambiguo e il compilatore ha ragione a lamentarsi.


1
Come è letterale1-1 un numero intero ? È un'espressione contenente due letterali interi con valore 1e un -operatore.
noce

@walnut: Probabilmente ti riferisci all'espressione scomoda "sequenza di cifre binarie, cifre ottali, cifre o cifre esadecimali" . Questa è una formulazione molto sfortunata per qualcosa di piuttosto "ovvio", che suggerisce qualcosa che non è il caso (cioè escludere il carattere meno). Con solo "cifre", e pedantemente per la definizione di "cifre" (una delle 0 ... 9), non è possibile avere qualsiasi letterali negativi (come ad esempio -1). Il che, dato che il tipo predefinito è firmato , tuttavia, è ovviamente necessario, ed è manifestamente possibile (e universalmente accettato).
Damon,

1
Mi riferisco alla grammatica per intero-letterale mostrata nel link standard, che non corrisponde 1-1. C ++ non ha valori letterali interi negativi. -1è un'espressione composta da un 1intero letterale (di tipo firmato) e da un -operatore meno unario. Vedi anche la sezione "Note" su cppreference.com .
noce

È certamente vero che la grammatica non ce l'ha, ma è ampiamente insignificante. Per necessità e per definizione, C ++ ha letteralmente valori negativi, poiché se non si aggiunge esplicitamente u, il valore letterale è, per definizione, firmato. I tipi firmati hanno valori negativi (circa il 50% dei valori possibili sono negativi). È un peccato che la grammatica (per un motivo che non saprei) sia fuorviante in questo modo, e sebbene tecnicamente (secondo la grammatica) -1 sia un letterale positivo, negato, con tutti gli altri mezzi è ovviamente negativo letterale. Proprio come 3 + 4 è letterale.
Damon,

A proposito, ci ho provato 0U. Stesso problema. Quello che non ho provato è un enumvalore. Forse uno di nome avrebbe cambiato le cose. Ho finito per scrivere una lunga espressione con decltypee remove_reference.
user1334767
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.