Реализация макроса __is_constexpr (ICE_P) ядра Linux на чистом C ++ - PullRequest
3 голосов
/ 21 апреля 2019

После чтения о стандартной версии C11 для предиката ICE_P Мартина Иккера я попытался реализовать его на чистом C ++. Версия C11 с использованием выбора _Generic выглядит следующим образом:

#define ICE_P(x) _Generic((1? (void *) ((x)*0) : (int *) 0), int*: 1, void*: 0)

Очевидный подход для C ++ состоит в замене _Generic на шаблон и decltype, например:

template<typename T> struct is_ice_helper;
template<> struct is_ice_helper<void*> { enum { value = false }; };
template<> struct is_ice_helper<int*>  { enum { value = true  }; };

#define ICE_P(x) (is_ice_helper<decltype(1? (void *) ((x)*0) : (int *) 0)>::value)

Однако он не проходит самый простой тест . Почему он не может обнаружить целочисленные константные выражения?

1 Ответ

2 голосов
/ 21 апреля 2019

Проблема тонкая. Спецификация для определения составного типа операндов указателя условного выражения в C ++ похожа на те, что в C, поэтому она выглядит многообещающе:

(N4659) [expr.cond]

7 L-значение-в-значение, массив-указатель и функция-указатель Стандартные преобразования выполняются на втором и третьем операндах. После этих преобразований должно быть выполнено одно из следующих действий:

  • [...]

  • Один или оба из второго и третьего операндов имеют тип указателя; преобразование указателя, преобразование указателя функции и квалификация преобразования выполняются, чтобы привести их к составному указателю тип (пункт [expr]). Результат имеет тип составного указателя.

  • [...]

Редукция к типу составного указателя указывается следующим образом:

(N4659) [expr]

5 Составной указатель типа двух операндов p1 и p2, имеющих типы T1 и T2 соответственно, где хотя бы один является указателем или указатель на тип члена или std​::​nullptr_­t, это:

  • если p1 и p2 являются константами нулевого указателя, std​::​nullptr_­t;
  • если p1 или p2 - константа нулевого указателя, T2 или T1 соответственно;
  • если T1 или T2 - «указатель на cv1 void», а другой тип - «указатель на cv2 T», где T - тип объекта или void, «указатель на cv12 void», где cv12 - объединение cv1 и cv2;
  • [...]

Таким образом, результат нашего макроса ICE_P определяется тем, с какой из пуль выше мы высаживаем одну после проверки каждой по порядку. Учитывая то, как мы определили is_ice_helper, мы знаем, что составной тип не является nullptr_t, в противном случае мы нажмем первый маркер и получим ошибку из-за отсутствующей специализации шаблона. Таким образом, мы должны попасть в пулю № 3, делая предикатный отчет ложным. Кажется, все зависит от определения константы нулевого указателя.

(N4659) [conv.ptr] (выделено мной)

1 A константа нулевого указателя - это целочисленный литерал со значением ноль или значение типа std​::​nullptr_­t. Нулевой указатель константа может быть преобразована в тип указателя; результат нулевой значение указателя этого типа и отличается от любого другого значение указателя объекта или типа указателя на функцию. Такое преобразование называется преобразованием нулевого указателя. Два значения нулевого указателя одного и того же Тип должен сравниваться равным. Преобразование константы нулевого указателя в указатель на cv-квалифицированный тип - это одиночное преобразование, а не последовательность преобразования указателя с последующей квалификацией преобразование. Константа нулевого указателя целочисленного типа может быть преобразована до значения типа std​::​nullptr_­t.

Так как (int*)0 не является константой нулевого указателя по вышеприведенному определению, мы не можем претендовать на первый пункт [expr] / 5. Составной тип не std::nullptr_t. (void *) ((x)*0) не является константой нулевого указателя и не может быть преобразована в единицу. Удаление актерского состава (что не разрешено определением) оставляет нам (x)*0. Это целочисленное константное выражение со значением ноль. Но это не целочисленный литерал со значением ноль! Определение константы нулевого указателя в C ++ отличается от определения в C!

(N1570) 6.3.2.3 Указатели

3 Целочисленное константное выражение со значением 0 или такое выражение, приведенное к типу void *, называется константой нулевого указателя. Если константа нулевого указателя преобразуется в тип указателя, в результате указатель, называемый нулевым указателем, гарантированно сравнивает неравное с указатель на любой объект или функцию.

C аллоws произвольные константные выражения со значением ноль для формирования константы нулевого указателя, в то время как C ++ требует целое число литералы .Учитывая богатую поддержку C ++ для вычисления константных выражений различных литеральных типов, это кажется ненужным ограничением.И тот, который делает вышеуказанный подход к ICE_P не стартером в C ++.

...