2014-01-30 4 views
15

Из того, что я могу сказать, сообщество SO делится на то, объявляет ли функция noexcept значимые оптимизаторы компилятора, которые в противном случае были бы невозможны. (Я говорю конкретно о компиляторах оптимизации, а не оптимизации реализации библиотек на основе move_if_noexcept.) Для этого вопроса давайте предположим, что noexcept действительно делает возможной оптимизацию генерации кода. С этим допущением имеет смысл объявить inline функции noexcept? Предполагая, что такие функции фактически встроены, это, по-видимому, требует, чтобы компиляторы генерировали эквивалент блока try вокруг кода, полученного в результате функции inline на сайте вызова, потому что, если в этой области возникает исключение, необходимо вызвать terminate. Без noexcept, что try блок, казалось бы, не нужен.Имеет ли смысл объявлять встроенные функции noexcept?

Мой первоначальный интерес был сделан ли смысл объявлять функции лямбда noexcept, учитывая, что они неявно inline, но потом я понял, что одни и те же проблемы возникают при любом inline функции, а не только Лямбдами.

+0

'try'-блок подразумевает стек разматывания. Вызов 'terminate' не делает. – dyp

+3

Эффективность в стороне, это может быть ситуационно полезным, если функция действительно должна быть небезопасной. – templatetypedef

+0

@dyp: Когда я написал «эквивалент блока« try », я пытался выразить мысль о том, что код все равно должен быть сгенерирован для обнаружения исключений во время выполнения и для вызова« прекратить », если они возникнут. Я согласен с тем, что сгенерированный код не должен делать именно то, что бы сделал настоящий блок try. – KnowItAllWannabe

ответ

10

давайте предположим, что noexcept делает оптимизацию смысл генерации кода возможных

OK

Предполагая, что такие функции фактически встраиваемой, это, казалось бы требует, чтобы компиляторы генерируют эквивалент попробуйте заблокировать около код, полученный в результате встроенной функции на сайте вызова, потому что , если в этой области возникает исключение

Не обязательно, поскольку компилятор может смотреть на тело функции и видеть, что он ничего не может выбросить. Поэтому номинальная обработка исключений может быть устранена.

Если функция «полностью» встроена (то есть, если встроенный код не содержит вызовов функций), я бы ожидал, что компилятор может довольно часто делать это определение, но не, например, в случае, когда есть вызовите vector::push_back(), и писатель функции знает, что зарезервировано достаточное пространство, но компилятор этого не делает.

Помните также, что в хорошей реализации блок try может фактически не потребовать выполнения какого-либо кода в случае, когда ничего не выбрасывается.

С этим допущением имеет смысл объявлять встроенные функции noexcept?

Да, чтобы получить предполагаемые оптимизации noexcept.

+0

Ваш комментарий о компиляторах, оптимизирующих блоки try, проверяя их содержимое, не зависит от этого вопроса. Компиляторы всегда могут выполнять такую ​​оптимизацию. – KnowItAllWannabe

+0

@KnowItAllWannabe: Я думаю, что это относится к вопросу. Если блок try имеет нулевую стоимость, то блок try становится * нерелевантным * к рассмотрению того, стоит ли использовать «noexcept», и насколько я могу сказать, вопрос в основном уходит. Конечно, даже если код не выполняется, он может не иметь нулевой стоимости, поскольку обработка исключений может раздувать двоичный файл. Но тогда, inlining может раздувать бинарный тоже и тем не менее, как правило, это повышает производительность. –

+1

Лучшим примером невозможного исключения будет 'bad_alloc' на' vector :: push_back() 'после достаточного' vector :: reserve() '. Практически невозможно доказать для компилятора, довольно тривиальную для человека. – MSalters

2

Стоит отметить, что в кругах власти было интересное обсуждение проблем, не связанных с норой.Я настоятельно рекомендую прочитать эти:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3227.html

http://www.stroustrup.com/N3202-noexcept.pdf

По-видимому, весьма влиятельные люди заинтересованы в добавлении какой-то автоматической nothrow дедукции C++.

После некоторого раздумья я изменил свою позицию почти напротив, смотри ниже

Рассмотрим это:

  • при вызове функции, имеющей noexcept на декларацию - вы извлечь выгоду из этого (не нужно иметь дело с возможностью разворота и т. д.)

  • , когда компилятор компилирует функцию, которая имеет noexcept по адресу Определить - (если компилятор не может доказать, что функция действительно не очень важна) производительность (теперь компилятор должен гарантировать, что исключение не сможет избежать этой функции). Вы просите его выполнить обещания без исключений

I.e. noexcept оба причиняют вам боль и приносят пользу. Это не так, если функция встроена! Когда он встроен - нет никакой пользы от noexcept от объявления вообще (декларация и определение становятся одной вещью). То есть, если вы на самом деле не хотите, чтобы компилятор обеспечивал это ради безопасности. А по безопасности Я имею в виду , вы скорее закончите, чем произведете неправильный результат.

Это должно быть очевидно, сейчас - нет никакого смысла объявляя встраиваемыми функции noexcept (имейте в виду, что не каждый рядный функция собирается получить встраиваемыми).

Давайте посмотрим на различные категории функций, которые не бросают (вы просто знаете, что они не делают):

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

  2. не-встроенный, компилятор не может доказать, что он не выбрасывает - noexcept может повредить функциональный орган, но преимущество (трудно сказать, что более выгодно)

  3. встраиваемого, компилятор может доказать, что он не бросает - noexcept не служит никакой цели

  4. встраиваемой, компилятор не может доказать, что он не бросает - noexcept повредит сайт вызова

Как видите, nothrow - это просто плохо разработанная языковая функция. Он работает только в том случае, если вы хотите выполнить обещание без исключений. Невозможно использовать его - он может дать вам «безопасность», но не производительность.

noexcept ключевое слово в конечном итоге используется как в качестве обещания (по объявлению), так и в отношении принудительного исполнения (по определению) - не идеальный подход, я думаю (lol, второй удар по спецификациям исключения и мы все равно не поняли его) ,

Итак, что делать?

  1. объявить свое поведение (увы, язык не имеет ничего, чтобы помочь вам здесь)! Например:

    void push_back(int k); // throws only if there is no unused memory 
    
  2. не ставят noexcept на встроенных функций (если это не вряд ли будет встраиваемыми, например, очень большой)

  3. для не встроенных функций (или функция, которая вряд ли быть встроенным) - позвонить. Чем больше функция получает, тем меньше noexcept "отрицательный эффект S становится (сравнительно) - в какой-то момент он, вероятно, имеет смысл указать его для вызывающих абонентов выгоды

  4. использования noexcept на ходу конструктора и перемещения оператора присваивания (и деструктор?). Это может повлиять на них отрицательно, но если вы этого не сделаете - некоторые функции библиотеки (std :: swap, некоторые операции с контейнерами) не будут использовать наиболее эффективный путь (или не будут предоставлять лучшую гарантию исключения). В принципе, любое место, которое использует noexceptОператор на вашей функции (на данный момент) заставит вас использовать noexceptспецификатор.

  5. использование noexcept если вы не доверяете вызывает ваша функция делает и скорее умру, чем он себя неожиданно

  6. чистые виртуальные функции - чаще всего вы не доверяете людям, реализующие эти интерфейсы. Часто это делает страхование смысл покупки (указав noexcept)

Ну, как еще noexcept могут быть разработаны?

  1. Я хотел бы использовать два различные ключевые слова - один для объявления обещания, а другое для обеспечения соблюдения его. Например. noexcept и force_noexcept. Фактически, принудительное выполнение на самом деле не требуется - это может быть сделано с помощью try/catch + terminate() (да, он разматывает стек, но кто волнуется, если за ним следует std :: terminate()?)

  2. Я бы заставил компилятор проанализировать все вызовы в заданной функции, чтобы определить, может ли она быть выбрана. Если это так, и было сделано безусловное обещание - ошибка компилятора будет испускаться

  3. Код, который может быть брошен, но вы знаете, что этого не должно быть, чтобы обеспечить компилятор, чтобы он был в порядке. Smth так:

    vector<int> v; 
    v.reserve(1); 
    ... 
    nothrow {  // memory pre-allocated, this won't throw 
        v.push_back(10); 
    } 
    

    если обещание нарушается (то есть кто-то изменил вектор кода и теперь предоставляет другие гарантии) - неопределенное поведение.

Отказ от ответственности: этот подход может быть слишком непрактично, кто знает ...

Смежные вопросы