Преобразование из интегрального постоянного выражения в нуль-указатель

Рассмотрим следующий код:

#include <memory>

void f( std::shared_ptr<int> ) {}

int main()
{
    f( 0 );               // compiles fine in gcc and clang
    f( 1 - 1 );           // compiles fine in gcc, fails in clang
    constexpr int i = 0;
    f( i );               // fails to compile in gcc and clang
    f( i - 0 );           // compiles fine in gcc, fails in clang
}

почему только f( i ) не удается скомпилировать, хотя i следует оценивать как константу времени компиляции со значением 0?

PS проверяется с помощью g++ v 5.1.0, он принимает все варианты, кроме f(i); в обоих режимах С++ 11 и С++ 14 PPS проверяется с помощью clang 3.7, он отклоняет все варианты, кроме буквального 0, как в режиме С++ 11, так и в режиме С++ 14

Ответы

Ответ 1

Это ошибка gcc. Отчет о дефектах 903: константные константные константы указателя указателя, зависящие от стоимости, который представляет собой отчет о дефекте против С++ 11 (он имеет статус CD3), делает его таким что только целочисленный литерал 0 считается константой нулевого указателя.

Он изменил раздел 4.10 [conv.ptr] paragraph 1 среди других изменений:

Константа нулевого указателя является интегральным постоянным выражением (5.19 [expr.const]) prvalue целочисленного типа, который вычисляется до нуля [...]

в

Константа нулевого указателя представляет собой целочисленный литерал (2.14.2 [lex.icon]) со значением 0 [...]

Это указано как несовместимость с С++ 03, из раздела C.2.2 Раздел 4: стандартные преобразования [diff.cpp03.conv], в котором говорится:

Изменить: Только литералы являются целыми константами указателя на указатель
Обоснование: Удаление неожиданных взаимодействий с шаблонами и постоянные выражения
Влияние на оригинальную функцию: Действительный код С++ 2003 может не скомпилироваться или дают разные результаты в этом Международном стандарте, поскольку следующий пример иллюстрирует:

void f(void *); // #1
void f(...); // #2
template<int N> void g() {
  f(0*N); // calls #2; used to call #1
}

Следующий отчет об ошибке gcc [С++ 11] [DR 903] нулевое целочисленное выражение константы должен предпочесть преобразование в указатель, показывает, что команда gcc изначально считала, что это было изменение С++ 17, но позже изменило его действие на С++ 11.

Мы можем видеть в главном редакторе gcc (6.0) это исправлено (видеть его в прямом эфире) и производит диагностику для всех случаев clang делает:

error: could not convert '(1 - 1)' from 'int' to 'std::shared_ptr<int>'
 f( 1 - 1 );           // compiles fine in gcc, fails in clang
    ~~^~~

error: could not convert 'i' from 'const int' to 'std::shared_ptr<int>'
 f( i );               // fails to compile in gcc and clang
      ^

error: could not convert '(0 - 0)' from 'int' to 'std::shared_ptr<int>'
 f( i - 0 );           // compiles fine in gcc, fails in clang
    ~~^~~

Ответ 2

Поскольку константа нулевого указателя определяется не только как интегральная константа времени компиляции со значением 0, а как целое число литерал со значением 0 (или как значение класса std::nullptr_t), курс). С++ 14 (N4140), 4.10/1.

Таким образом, только первая строка f(0) должна компилироваться, а все остальные должны вызывать хотя бы диагностическое сообщение от соответствующего компилятора.