Является ли std::abs(0u) неправильным?

Учитывая следующую программу:

#include <cmath>

int main()
{
    std::abs(0u) ;
}

gcc и clang расходятся во мнениях относительно того, является ли это неправильным. При использовании gcc с libstdc++ код создается без ошибок и предупреждений (посмотреть вживую), при использовании clang с libc++ возникает следующая ошибка (посмотреть вживую):

error: call to 'abs' is ambiguous
std::abs(0u) ;
^~~~~~~~

Какой результат правильный? Должен ли abs(0u) быть двусмысленным или нет?


MSalters указывает на интересный связанный вопрос: Шаблонная версия std::abs.


person Shafik Yaghmour    schedule 20.04.2015    source источник


Ответы (1)


Похоже, что libstdc++ правильно, это правильно, хотя мы увидим, что есть некоторые сомнения относительно того, является ли это дефектом в активной проблеме LWG 2192.

В черновом стандартном разделе 26.8 [c.math] 11 раздела стандарта C++11 говорится:

При этом должны быть предусмотрены дополнительные перегрузки, достаточные для обеспечения:

и включает в себя следующий пункт:

  1. В противном случае, если какой-либо аргумент, соответствующий параметру double, имеет тип double или целочисленный тип, то все аргументы, соответствующие параметрам double, фактически преобразуются в тип double.

и мы видим, что это libstdc++ делает действительно предусмотреть для этого случай:

template<typename _Tp>
inline typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
                                                  double>::__type
abs(_Tp __x)
{ return __builtin_fabs(__x); }

Существует также отчет об ошибке gcc std::abs (long long) прибегает к std ::abs (double), если llabs отсутствует, что ставит под сомнение правильность этой реализации, и в одном ответе говорится:

[...] соответствует стандарту, любое целое число должно безоговорочно становиться двойным. [...]

Отчет об ошибке в конечном итоге привел к активной проблеме LWG 2192: Действительность и возвращаемый тип std::abs(0u) неясны при подаче, что среди прочего говорит:

  1. В C++11 можно считать, что дополнительное правило «достаточной перегрузки» из 26.8 [c.math] p11 (см. также LWG 2086) применимо и к перегрузкам std::abs(), что может привести к следующему: возможные выводы:

Программа

    #include <type_traits>
    #include <cmath>

    static_assert(std::is_same<decltype(std::abs(0u)), double>(), "Oops");

    int main() {
      std::abs(0u); // Calls std::abs(double)
    }

должен быть правильно сформирован из-за подпункта 2 ("[..] или целочисленный тип [..]") 26.8 [c.math] p11 (обратите внимание, что текущая резолюция LWG 2086 не исправить эту проблему).

  1. Любая единица перевода, включающая оба и может быть неправильно сформирована из-за двух конфликтующих требований к возвращаемому типу перегрузки std::abs(int).

Мне кажется, что по крайней мере второй исход не предполагается, лично я считаю, что оба неудачны [...] Также следует отметить, что соответствующий набор правил «функции универсального типа» из C99/C1x в 7.25 p2+ 3 ограничен функциями с плавающей запятой из и , поэтому его нельзя применять к функциям abs (но к функциям fabs!).

Вопрос в том, предназначалось ли это также для abs. Это может быть дефектом, так как, похоже, нет способа интерпретировать текущую формулировку, чтобы исключить abs.

Таким образом, текущая формулировка указывает на то, что libstdc++ соответствует, неясно, почему libc++ выбрала свою текущую реализацию такой, какая она есть. Я не могу найти ни отчетов об ошибках, ни дискуссий по этой теме, а в проблеме LWG не упоминаются расходящиеся реализации.

Предлагаемое решение сделает std::abs(0u) неправильным:

Если abs() вызывается с аргументом целочисленного типа без знака, который не может быть преобразован в int путем целочисленного преобразования ([conv.prom]), программа имеет неверный формат. [Примечание: аргументы, которые могут быть преобразованы в int, разрешены для совместимости с C. — примечание в конце]

Хотя некоторые могут усомниться в использовании abs с беззнаковым типом, Говард Хиннант указывает в отчете, что при использовании шаблонов такие последствия могут быть неочевидными, и приводит пример:

[...] особенно в C++, где у нас есть шаблоны, и задействованные типы не всегда очевидны для программиста во время разработки. Например, рассмотрим:

template <class Int>
Int
analyze(Int x, Int y)
{
  // ...
  if (std::abs(x - y) < threshold)
  {
    // ...
  }
  // ...
}
person Shafik Yaghmour    schedule 20.04.2015
comment
2192 перемещено в дефект - person Shafik Yaghmour; 31.05.2017