Почему char {} и char() работают как временная переменная для аргумента char *?

В Visual C++ 2017 (с /std:C++14 или с /std:C++17) работает следующий код:

void TakePtr(char*); // const or not

int main()
{ 
     TakePtr(char{});
     TakePtr(char());
}

Я не понимаю, почему это работает.

По-видимому, следующее также будет работать (как и ожидалось):

void TakeChar(char);

   TakeChar(char{});
   TakeChar(char());

Как компилятор выводит (или конвертирует) тип char в char*, когда char{} или char() используются в качестве аргумента?

Теперь, если у меня есть перегрузки char и char*, это работает без каких-либо ошибок/предупреждений о неоднозначности:

void TakePtr(char*);
void TakePtr(char);

    TakePtr(char{});  // Chooses 'char'
    TakePtr(char());  // Chooses 'char'

Почему компилятор в порядке с char{} для TakePtr(char*)? И почему он не выдает предупреждение/ошибку при выборе лучшей версии? Такое поведение неизбежно нарушает существующий код.

Конечно, компилятор не доволен:

void TakePtr(char*);

    char c{};
    TakePtr(c);

Ответ 1

Потому что Visual много врет. Особенно старше. Ваш код предлагает clang сообщить об ошибке:

<source>:9:6: error: no matching function for call to 'TakePtr'

     TakePtr(char{});

     ^~~~~~~

<source>:5:6: note: candidate function not viable: no known conversion from 'char' to 'char *' for 1st argument

void TakePtr(char*); // const or not

     ^

<source>:10:6: error: no matching function for call to 'TakePtr'

     TakePtr(char());

     ^~~~~~~

<source>:5:6: note: candidate function not viable: no known conversion from 'char' to 'char *' for 1st argument

void TakePtr(char*); // const or not

     ^

2 errors generated.

Visual, как известно, "шаткий" с точки зрения следования стандарту C++, поэтому не стоит слишком на него полагаться. Попробуйте проверить с помощью clang/gcc, просто чтобы быть уверенным.

Ответ 2

Компилятор gnu c версии 6.3.0 не принимает ваш код, что приводит к появлению '-fpermissive', поэтому я предполагаю, что это будет работать только с Visual C++.

Ответ 3

Это просто отставание от MSVC: в С++ 03 было правило, что любое константное выражение целочисленного типа и значения 0 является константой нулевого указателя и, следовательно, может быть преобразовано в char*. Конечно, char() подходит - и char{} означает то же самое, хотя оно никогда не пересекалось с правилом.