int numeral -> regras de conversão de ponteiros

19

Considere o seguinte código.

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

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

O MSVC 2017 não compila isso. Parece que há uma chamada sobrecarregada ambígua, como 1-1é a mesma 0e, portanto, pode ser convertida double*. Outros truques, como 0x0, 0L, ou static_cast<int>(0), não quer trabalhar. Mesmo declarando a const int Zero = 0e chamando f(Zero)produz o mesmo erro. Só funciona corretamente se Zeronão estiver const.

Parece que o mesmo problema se aplica ao GCC 5 e abaixo, mas não ao GCC 6. Estou curioso para saber se isso faz parte do padrão C ++, um bug conhecido do MSVC ou uma configuração no compilador. Um Google superficial não produziu resultados.

user1334767
fonte

Respostas:

18

O MSVC considera 1-1uma constante de ponteiro nula. Isso estava correto pelo padrão do C ++ 03, onde todas as expressões constantes integrais com valor 0eram constantes de ponteiro nulas, mas foi alterado para que apenas literais inteiros zero sejam constantes de ponteiro nulas para C ++ 11 com o problema 903 do CWG . Esta é uma mudança radical , como você pode ver no seu exemplo e também está documentado no padrão, consulte [diff.cpp03.conv] do padrão C ++ 14 (rascunho N4140).

A MSVC aplica essa alteração apenas no modo de conformidade. Portanto, seu código será compilado com o /permissive-sinalizador, mas acho que a alteração foi implementada apenas no MSVC 2019, veja aqui .

No caso do GCC, o GCC 5 é padronizado para o modo C ++ 98, enquanto o GCC 6 e posterior são padronizados para o modo C ++ 14, e é por isso que a mudança de comportamento parece depender da versão do GCC.

Se você chamar fcom uma constante de ponteiro nulo como argumento, a chamada será ambígua, porque a constante de ponteiro nulo pode ser convertida em um valor de ponteiro nulo de qualquer tipo de ponteiro e essa conversão tem a mesma classificação que a conversão de int(ou qualquer tipo integral) para double.

noz
fonte
-1

O compilador funciona corretamente, de acordo com [over.match] e [conv] , mais especificamente [conv.fpint] e [conv.ptr].

Uma sequência de conversão padrão é [blá blá] zero ou uma [...] conversão integral flutuante, conversão de ponteiro, [...].

e

Um pré-valor de um tipo inteiro ou de um tipo de enumeração sem escopo pode ser convertido em um pré-valor de um tipo de ponto flutuante. O resultado é exato, se possível [blá blá]

e

Uma constante de ponteiro nulo é um literal inteiro com valor zero ou [...]. Uma constante de ponteiro nulo pode ser convertida em um tipo de ponteiro; o resultado é o valor de ponteiro nulo desse tipo [blá blá]

Agora, a resolução de sobrecarga é escolher a melhor correspondência entre todas as funções candidatas (que, como um recurso divertido, nem precisam ser acessíveis no local da chamada!). A melhor correspondência é aquela com parâmetros exatos ou, alternativamente, o menor número possível de conversões. Podem ocorrer zero ou uma conversão padrão (... para cada parâmetro) e zero é "melhor" que um.

(1-1)é um literal inteiro com valor 0.

Você pode converter o literal inteiro zero em cada um doubleou double*(ou nullptr_t), com exatamente uma conversão. Portanto, supondo que mais de uma dessas funções seja declarada (como é o caso no exemplo), existe mais de um candidato e todos os candidatos são igualmente bons, não existe melhor correspondência. É ambíguo, e o compilador tem razão em reclamar.

Damon
fonte
11
Como é 1-1um literal inteiro ? É uma expressão que contém dois literais inteiros com valor 1e um -operador.
walnut
@ walnut: Você provavelmente se refere à estranha frase "sequência de dígitos binários, dígitos octais, dígitos ou dígitos hexadecimais" . É uma expressão muito infeliz para algo bastante "óbvio", o que sugere algo que não é o caso (por exemplo, excluindo o caractere de menos). Com apenas "dígitos", e pedante pela definição de "dígitos" (um de 0 ... 9), não é possível ter qualquer literais negativos (tais como -1). O qual, como o tipo padrão é assinado , no entanto, é obviamente necessário, e é comprovadamente possível (e universalmente aceito) também.
Damon
11
Refiro-me à gramática do literal inteiro mostrada no link padrão, que não corresponde 1-1. C ++ não possui literais inteiros negativos. -1é uma expressão composta por um 1literal inteiro (do tipo assinado) e um -operador menos unário. Consulte também a seção "Notas" em cppreference.com .
walnut
Certamente é verdade que a gramática não a possui, mas isso é amplamente inconseqüente. Por necessidade e por definição, o C ++ possui literais negativos, uma vez que, a menos que você inclua explicitamente u, seu literal é, por definição, assinado. Os tipos assinados têm valores negativos (cerca de 50% dos valores possíveis são negativos). É lamentável que a gramática (por uma razão que eu não saberia) seja enganosa dessa maneira e, embora tecnicamente (de acordo com a gramática) -1 seja um literal positivo, negado, por todos os outros meios, é claro que é negativo literal. Muito parecido com 3 + 4 é literal.
Damon
A propósito - eu tentei 0U. Mesmo problema. O que eu não tentei é um enumvalor. Talvez um nomeado tivesse mudado as coisas. Acabei escrevendo uma longa expressão com decltypee remove_reference.
User1334767 25/03