`noexcept` поведение` constexpr` функций - PullRequest
6 голосов
/ 08 марта 2020

Формулировка [expr.unary.noexcept] изменена в C ++ 17 .


Ранее ( n4140, 5.3. 7 оператор noexcept [expr.unary.noexcept] ), мой акцент :

Результат оператора noexcept равен false, если в потенциально оцененном контексте выражение будет содержать

(3.1) потенциально вычисляемый вызов функции, функции-члена, указателя функции или указателя на функцию-член который не имеет спецификации исключения без выброса ([exc.spec]), , если только вызов не является константным выражением ([expr.const]) ...


Сейчас 1 ( 7.6.2.6 Оператор noexcept [expr.unary.noexcept] ):

Результат оператора noexcept равен true , если только выражение не является потенциально-генерирующим ([исключением.spec]).

И затем в 14.5 Спецификации исключений [кроме.spec] :

Если объявление функции не имеет спецификатора noexcept, объявление имеет потенциально генерируемую спецификацию исключений, если только ...

, но , если только список из 14,5 (3) не перечисляет constexpr, оставляя его как потенциально бросающий ...

1 ссылка на C ++ 17 n4659 добавлено LF в комментарии.


Тестовый код

constexpr int f(int i) { return i; }

std::cout << boolalpha << noexcept(f(7)) << std::endl;
int a = 7;
std::cout << boolalpha << noexcept(f(a)) << std::endl;

, используемый для печати ( с g cc 8.3 ):

true
false

как при компиляции с -std = c ++ 11 и -std = c ++ 2a


Однако теперь печатает тот же код ( с g cc 9.2 ):

false
false

оба при компиляции с -std = c ++ 11 и -std = c ++ 2a


Clang, кстати, очень непротиворечив, начиная с 3.4.1 и идет с:

false
false

  • Каково правильное поведение для каждого spe c?
  • Произошло ли реальное изменение в spe c? Если да, то в чем причина этого изменения?
  • Если есть изменение в спецэффекте c, которое влияет или противоречит прошлому поведению, будет ли обычной практикой подчеркивать , что изменить и его последствия? Если изменение не выделено , может ли это означать, что это может быть недосмотр ?
  • Если это действительно предполагаемое изменение, считалось ли оно исправлением ошибки, которое должно go Возвращаясь к предыдущим версиям spe c, правы ли компиляторы с обратной привязкой нового поведения к C ++ 11?

Примечание на стороне: noexcept вычет по constexpr функции влияет на этот трюк .

1 Ответ

5 голосов
/ 08 марта 2020

Сводка

Каково правильное поведение для каждого спе c?

true false до C ++ 17, false false после C ++ 17.

Произошли ли реальные изменения в спецификации c? Если да, то в чем причина этого изменения?

Да. См. Цитату из отчета об ошибке Clang ниже.

Если в spe c есть изменение, которое затрагивает или противоречит прошлому поведению, было бы обычной практикой подчеркивать это изменение и его последствия ? Если изменение не подчеркивается, может ли это означать, что это может быть недосмотр?

Да; да (но CWG нашла причину, чтобы оправдать упущение позже, поэтому она была сохранена как есть).

Если это действительно предполагаемое изменение, считалось ли это исправлением ошибки, которое должно go Возвращаясь к предыдущим версиям spe c, верны ли компиляторы с обратной привязкой нового поведения к C ++ 11?

Я не уверен. См. Цитату из отчета об ошибке Clang ниже.

Detail

Я искал много мест, и пока что самое близкое, что я могу найти, это комментарии к соответствующим сообщениям об ошибках:

  • G CC Ошибка 87603 - [C ++ 17] noexcept больше не используется специально для константных выражений

    CWG 1129 (который в конечном итоге в C ++ 11) добавлен специальный случай в noexcept для константных выражений, так что:

    constexpr void f() {} static_assert(noexcept(f()));
    

    CWG 1351 (который закончился в C ++ 14) значительно изменил формулировку, но особый случай остался в другом виде.

    P0003R5 (который закончился в C ++ 17) снова изменил формулировку, но особый случай был удален (случайно), так что теперь:

    constexpr void f() {} static_assert(!noexcept(f()));
    

    Согласно Ричарду Смиту в LLVM 15481, CWG обсуждала это, но решила сохранить поведение как есть. В настоящее время clang делает правильные вещи для C ++ 17 (и терпит неудачу для C + +14 и C ++ 11, специально). g ++, однако, уже реализовал специальный случай для C ++ 11, но не для C ++ 17. В настоящее время i cc и msv c, похоже, ведут себя как g ++.

  • Ошибка Clang 15481 - noexcept должен проверять, является ли выражение константным выражением

    Специальный случай с константным выражением был удален - очевидно, случайно - wg21.link/p0003. Я расследую, останется ли он в прошлом или нет.

    Делали ли вы что-нибудь, чтобы избежать quadrati c времени выполнения для глубоко вложенных выражений?

    [...]

    Вывод из обсуждения CWG: мы собираемся оставить все как есть. noexcept не имеет специального правила для константных выражений.

    Оказывается, это действительно необходимо для правильной работы библиотеки: например, если noexcept пытается вычислить свой операнд, то (например, ) is_nothrow_swappable нарушается созданием std::swap constexpr, потому что std::swap<T> затем часто заканчивается созданием экземпляра до завершения T.

    В результате этого я также рассмотрим это изменение как эффективный DR против C ++ 11 и C ++ 14 ... но я открыт для повторного рассмотрения, если мы увидим много жалоб пользователей.

Другими словами, специальное правило было случайно удалено P0003 , но CWG решила сохранить удаление.

...