2016-01-01 5 views
16

constexpr спецификатор imply noexcept спецификатор функции? Answer до the similar question говорит «да» относительно спецификатора inline, но Eric Niebler's article заставляет меня задуматься о возможном ответе на текущий. На мой взгляд, ответ может зависеть от контекста использования функции constexpr: является ли это контекстом постоянного выражения или контекстом времени выполнения, т. Е. Являются всеми параметрами функции, известной во время компиляции или нет.Составляет ли constexpr noexcept?

Я ожидал, что ответ будет «да», но simple check показывает, что это не так.

constexpr 
bool f(int) noexcept 
{ 
    return true; 
} 

constexpr 
bool g(int) 
{ 
    return true; 
} 

static_assert(noexcept(f(1))); 
static_assert(noexcept(g(2))); // comment this line to check runtime behaviour 

#include <cassert> 
#include <cstdlib> 

int 
main(int argc, char * []) 
{ 
    assert(noexcept(f(argc))); 
    assert(noexcept(g(argc))); 
    return EXIT_SUCCESS; 
} 
+0

@ cad anyways t он вопрос очень общий, не думаю, что есть хороший конкретный пример. – Orient

+0

Counter-example: 'constexpr void * foo (int n) {return n == 0? nullptr: operator new (n); } '. [Демо] (https://ideone.com/zlTsDI). –

+0

Я сделал это один раз, см. Http://stackoverflow.com/a/13305072/34509 –

ответ

13

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

Это предусмотрено в проекте C++ 14 стандартный раздел 7.1.5 constexpr спецификатор [dcl.constexpr], который говорит нам о том, что разрешено в функции constexpr:

Определение функции constexpr должны удовлетворять следующие ограничения:

  • не должно быть виртуальным (10.3);

  • его тип возврата должен быть буквальным;

  • каждый из его типов параметров должен быть буквальным;

  • его функция тело должно быть = удаление, = по умолчанию, или соединение-заявление, которое не содержит

    • ассемблерных четкости,

    • Готы заявление,

    • a try-block, или

    • определение переменной нелитерального типа или статической или нитки st продолжительность обработки или для которых не выполняется никакая инициализация.

, который, как мы видим, не запрещает throw и фактически запрещает очень мало, так как Relaxing constraints on constexpr functions предложение стало частью C++ 14.

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

For A не- шаблон, не-дефолтная функция constexpr или конструктор constexpr без шаблона, не являющийся дефолтом, , , если значения аргументов не существуют, так что вызов функции или конструктора может быть оцененным подвыражением выражения постоянной константы (5.19), программа плохо сформирована; нет требуется диагностика.

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

constexpr int f(bool b) 
    { return b ? throw 0 : 0; } // OK 
constexpr int f() { return f(true); } // ill-formed, no diagnostic required 

Таким образом, мы ожидаем, что выход на следующем примере:

#include <iostream> 

constexpr int f(bool b) { return b ? throw 0 : 0; } 

int main() { 
    std::cout << noexcept(f(1)) << "\n" 
       << noexcept(f(0)) << "\n" ; 
} 

(see it live with gcc):

0 
1 

Visual Studio через webcompiler также дает тот же результат. Как отмечал hvd, clang имеет ошибку, о чем свидетельствует отчет об ошибке noexcept should check whether the expression is a constant expression.

Дефект Report 1129

Defect report 1129: Default nothrow for constexpr functions задает тот же вопрос:

Функция constexpr не разрешается возвращаться через исключение.Это должно быть распознано, а функция, объявленная constexpr без явной спецификации исключения, должна обрабатываться как объявленная noexcept (true), а не обычная noexcept (false). Для шаблона функции, объявленного constexpr без явной спецификации исключения, его следует считать noexcept (true) тогда и только тогда, когда ключевое слово constexpr соблюдается при заданном экземпляре.

и ответ был:

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

и модифицированные 5.3.7 [expr.unary.noexcept] пункт 3 пули 1 (добавление с акцентом отметил):

потенциально оценивали call80 к функции, функции-члена, функция указатель, или указатель на функцию элемента, который не имеет не-метательные исключения спецификации (15,4 [except.spec]), , если вызов не является постоянным выражением (5.20 [expr.const]),

5

It is said из noexcept что:

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

Кроме того, о constexpr, it is true что:

оператор noexcept всегда возвращает истину для постоянного выражения

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

Во всяком случае, из документации, есть следующее интересное замечание о взаимосвязи между noexcept и constexpr:

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

EDIT: пример с GCC

Благодаря @hvd за его интересный комментарий/пример с GCC о моей последней цитате.

constexpr int f(int i) { 
    return i == 0 ? i : f(i - 1); 
} 

int main() { 
    noexcept(f(512)); 
    return noexcept(f(0)) == noexcept(f(0)); 
} 

Код выше возвращает 0, с предупреждением о том, что заявление noexcept(f(512)) не имеет никакого эффекта.
Комментируя этот оператор, который якобы не имеет эффекта, возвращаемое значение изменяется на 1.

EDIT: известная ошибка на лязге

Опять же, благодаря @hvd также для this связи, то есть о хорошо известной ошибке в лязге относительно кода, указанного в вопросе.

Цитата из сообщения об ошибке:

Quoth книге C++, [expr.unary.noexcept] p3:

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

Мы не применяем эту последнюю фразу.

+1

Возможно, интересный пример с GCC вашей последней цитаты: 'constexpr int f (int i) {return i == 0? i: f (i - 1); } int main() {noexcept (f (512)); return noexcept (f (0)) == noexcept (f (0)); } '. Это возвращает '0', с предупреждением, что выражение' noexcept (f (512)); 'не имеет никакого эффекта. Комментируя это утверждение, которое якобы не имеет эффекта, возвращаемое значение изменяется на '1'. – hvd

+0

@hvd Я думаю, что пример можно найти и в связанной документации, в любом случае, спасибо, интересный комментарий. – skypjack

+0

Что касается кода в вопросе, [который уже известен как ошибка в clang] (https://llvm.org/bugs/show_bug.cgi?id=15481). Обычно я отправляю его как отдельный ответ, но в этом случае я думаю, что он достаточно мал, чтобы вы могли включить его в свой ответ, если хотите. – hvd

2

Нет, в общем, нет.

Функция constexpr может быть вызвана в контексте non-constepr, в котором разрешено исключать исключение (за исключением, конечно, если вы указали вручную его noexcept(true)).

Однако, как часть постоянного выражения (например, в вашем примере), он должен вести себя так, как если бы он был указан как noexcept(true) (конечно, если оценка выражения приведет к тому, что исключение будет выбрано, это не может приведет к вызову std::terminate, поскольку программа еще не запущена, но вместо этого приводит к ошибке времени компиляции).

Как и следовало ожидать, ваш пример не вызывает статическое утверждение с MSVC и g ++. Я не уверен, что это ошибка в clang, или я пропускаю что-то в стандарте.

+0

Каким образом это подразумевает «noexcept (true)», если вы уже сказали, что эффект «noexcept (true)» не применяется? – hvd

+0

@hvd: Поскольку skypjack процитировал: применение оператора noexcept к функции constexpr в контексте constexpr (как в вопросе дает true, так же, как если бы эта функция была объявлена ​​(noexcept (true)). – MikeMB

+0

Функция объявлена ​​'noexcept (true) 'имеет два эффекта: один, он делает исключения завершающими программу. Во-вторых, вызов функции не влияет на результат оператора noexcept. Он не имеет этого первого эффекта. Он ведет себя аналогично к второму эффекту, но не совсем точно так же и в любом случае, что второй эффект не достигается путем неявного создания функции «noexcept (true)», это является дополнительным исключением в правилах оператора «noexcept», как показано в ответе @ skypjack. – hvd

2

Вы можете исключить исключение из функции constexpr. Он сконструирован таким образом, что разработчик может указать ошибку компилятору. Учтите, что у вас есть следующие функции:

constexpr int fast_sqrt(int x) { 
    return (x < 0) ? throw invalid_domain() : fast_sqrt_impl(x); 
} 

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

C Стандарт ++ говорит, что это в (5.20):

Условное выражение-е является постоянным выражением ядра, если при оценке е, следуя правилам абстрактной машины (1.9), будет оценивать одну не из следующих выражений:

- вбрасывания выражение (5,17)

+0

Ваш стандарт не полностью поддерживает ваш ответ, вам также нужно указать «7.1.5», как в [мой ответ] (http://stackoverflow.com/a/34558600/1708801), чтобы доказать, что это допустимый код , –

+0

@Shafik, я искал часть, которая имеет критерии для функции constexpr. Спасибо, что включили это! – Andrew

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