Почему ** не ** объявляет функцию `constexpr`? - PullRequest
58 голосов
/ 25 февраля 2011

Любая функция, которая состоит только из оператора возврата, может быть объявлена ​​constexpr и, таким образом, позволит выполнять оценку во время компиляции, если все аргументы constexpr и в ее теле вызываются только функции constexpr. Есть ли причина не объявлять любую такую ​​функцию constexpr?

Пример:

  constexpr int sum(int x, int y) { return x + y; }
  constexpr i = 10;
  static_assert(sum(i, 13) == 23, "sum correct");

Может ли кто-нибудь предоставитьпример, где объявление функции constexpr принесет какой-либо вред?


Некоторые первоначальные мысли:

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

Но что я не понимаю: если не должно быть веской причины для того, чтобы когда-либо объявлять функцию не constexpr, то почему бы не каждая функция в стандартной библиотеке объявлена ​​constexpr?(Вы не можете утверждать, что это еще не сделано, потому что еще не было достаточно времени, чтобы сделать это, потому что делать это для all не составляет никакого труда - вопреки решению для каждой отдельной функции, если сделать этоconstexpr или нет.) --- Мне известно, что N2976 сознательно не требует cstrs для многих стандартных типов библиотек, таких как контейнеры, поскольку это было бы слишком ограничивающим для возможных реализаций.Давайте исключим их из аргумента и просто удивимся: если тип в стандартной библиотеке действительно имеет constexpr cstr, почему не каждая функция, работающая с ним, объявляется constexpr?

В большинстве случаев вы также не можетеутверждают, что вы можете предпочесть не объявлять функцию constexpr просто потому, что вы не предполагаете никакого использования во время компиляции: потому что, если другие evtl.будет использовать ваш код, они могут увидеть такое использование, что вы этого не сделаете.(Но, конечно, для типов признаков и тому подобного.)

Так что я думаю, что должна быть веская причина и хороший пример для того, чтобы сознательно не объявлять функцию constexpr?

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

Вопрос Почему std::forward отбрасывает constexpr -ness? является частным случаем этого.

Ответы [ 3 ]

33 голосов
/ 25 февраля 2011

Функции могут быть объявлены только constexpr, если они подчиняются правилам для constexpr --- без динамического приведения, без выделения памяти, без вызовов функций, не являющихся constexpr и т. Д.

Объявлениефункция в стандартной библиотеке как constexpr требует, чтобы ВСЕ реализации подчинялись этим правилам.

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

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

14 голосов
/ 25 февраля 2011

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

Есть несколько основных проблем в получении частичной оценки работы. Во-первых, это значительно усложняет логику компилятора, потому что компилятор должен будет иметь возможность имитировать все операции, которые вы можете поместить в исполняемую программу во время компиляции. В худшем случае это требует наличия полного интерпретатора внутри компилятора, что затрудняет задачу (написание хорошего компилятора C ++) и усложняет его выполнение на несколько порядков.

Я считаю, что причина текущей спецификации о constexpr заключается просто в ограничении сложности компиляторов. Случаи, которыми он ограничен, довольно просто проверить. Нет необходимости реализовывать циклы в компиляторе (что может вызвать целый ряд других проблем, например, что произойдет, если вы получите бесконечный цикл внутри компилятора). Это также позволяет избежать того, что компилятору потенциально придется оценивать операторы, которые могут вызвать ошибки во время выполнения, например, следовать за неверным указателем.

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

Подводя итог, нет теоретической причины, по которой вы не могли бы частично оценить программы на C ++ во время компиляции. На самом деле, люди делают это все время. Оптимизирующие компиляторы, например, по сути, программы, которые пытаются сделать это в максимально возможной степени. Метапрограммирование шаблонов - это один из примеров, когда программисты на C ++ пытаются выполнить код внутри компилятора, и с шаблонами можно делать некоторые замечательные вещи частично, потому что правила для шаблонов образуют функциональный язык, который компилятору проще реализовать. Более того, если вы подумаете о компромиссе между часами автора компилятора и часами программирования, метапрограммирование шаблонов показывает, что если у вас все в порядке, заставляя программистов наклоняться назад, чтобы получить то, что они хотят, вы можете создать довольно слабый язык (систему шаблонов) и сохранить Сложность языка проста. (Я говорю «слабый» как «не особенно выразительный», не «слабый» в смысле теории вычислимости).

Надеюсь, это поможет!

3 голосов
/ 25 февраля 2011

Если функция имеет побочные эффекты, вы не хотели бы отмечать ее constexpr. Пример

Я не могу получить от этого неожиданных результатов, на самом деле это выглядит так: gcc 4.5.1 просто игнорирует constexpr

...