2014-01-23 2 views
42

я исследовал то, что разрешено в core constant expression*, который рассматривается в разделе 5.19Постоянные выражения пункт из draft C++ standard, который говорит:Почему константные выражения имеют исключение для неопределенного поведения?

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [ Note: An overloaded operator invokes a function.—end note ]:

и перечисляет исключения, в пуль, который следует и включает в себя (курсив мой ):

an operation that would have undefined behavior [ Note: including, for example, signed integer overflow (Clause 5), certain pointer arithmetic (5.7), division by zero (5.6), or certain shift operations (5.8) —end note ];

Ха? Почему постоянные выражения нужен этот раздел для покрытия undefined behavior? Есть ли что-то особенное в отношении константных выражений, для которых требуется неопределенное поведение, чтобы иметь специальное вырезание в исключениях?

Имеет ли данное предложение какие-либо преимущества или инструменты, которые у нас не были бы без него?

Для справки это выглядит как последняя ревизия предложения для Generalized Constant Expressions.

+0

Я думаю, что любая реализация может принимать неверно сформированные программы (которые затем имеют неопределенное поведение). То есть реализация может принимать постоянные выражения с UB в качестве расширения (например, с семантикой, определяемой реализацией, вместо UB). Указав программу как не сформированную, из нее требуется диагностическое сообщение. – dyp

+0

@ dyp: Но причина, по которой диагностика не требуется для UB в целом, заключается в том, что диагностировать ее очень сложно. –

+0

@ dyp стандарт определенно не ясен, хотя комментарий Говарда, с которым я связан, кажется, указывает на то, что это ожидаемое поведение. –

ответ

38

формулировка фактически является предметом defect report #1313, который говорит:

The requirements for constant expressions do not currently, but should, exclude expressions that have undefined behavior, such as pointer arithmetic when the pointers do not point to elements of the same array.

Резолюция является нынешняя формулировка у нас есть сейчас, так что это явно было задумано, так что же инструменты, что это дает нам?

Давайте посмотрим, что происходит, когда мы пытаемся создать constexpr переменную с выражением, которое содержит неопределенное поведение, мы будем использовать clang для всех следующих примеров. Этот код (see it live):

constexpr int x = std::numeric_limits<int>::max() + 1 ; 

производит следующее сообщение об ошибке:

error: constexpr variable 'x' must be initialized by a constant expression 
    constexpr int x = std::numeric_limits<int>::max() + 1 ; 
       ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
note: value 2147483648 is outside the range of representable values of type 'int' 
    constexpr int x = std::numeric_limits<int>::max() + 1 ; 
            ^

Этот код (see it live):

constexpr int x = 1 << 33 ; // Assuming 32-bit int 

производит эту ошибку:

error: constexpr variable 'x' must be initialized by a constant expression 
    constexpr int x = 1 << 33 ; // Assuming 32-bit int 
      ^ ~~~~~~~ 
note: shift count 33 >= width of type 'int' (32 bits) 
    constexpr int x = 1 << 33 ; // Assuming 32-bit int 
       ^

и этот код, который имеет неопределенное поведение в функции constexpr:

constexpr const char *str = "Hello World" ;  

constexpr char access(int index) 
{ 
    return str[index] ; 
} 

int main() 
{ 
    constexpr char ch = access(20) ; 
} 

производит эту ошибку:

error: constexpr variable 'ch' must be initialized by a constant expression 
    constexpr char ch = access(20) ; 
       ^ ~~~~~~~~~~~~ 

note: cannot refer to element 20 of array of 12 elements in a constant expression 
    return str[index] ; 
     ^

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

Мы можем использовать эту функцию для определения, будет ли выражение добавления вызвать переполнение через SFINAE, следующий надуманный пример был вдохновлен DYP-х clever answer here:

#include <iostream> 
#include <limits> 

template <typename T1, typename T2> 
struct addIsDefined 
{ 
    template <T1 t1, T2 t2> 
    static constexpr bool isDefined() 
    { 
     return isDefinedHelper<t1,t2>(0) ; 
    } 

    template <T1 t1, T2 t2, decltype(t1 + t2) result = t1+t2> 
    static constexpr bool isDefinedHelper(int) 
    { 
     return true ; 
    } 

    template <T1 t1, T2 t2> 
    static constexpr bool isDefinedHelper(...) 
    { 
     return false ; 
    } 
}; 


int main() 
{  
    std::cout << std::boolalpha << 
     addIsDefined<int,int>::isDefined<10,10>() << std::endl ; 
    std::cout << std::boolalpha << 
    addIsDefined<int,int>::isDefined<std::numeric_limits<int>::max(),1>() << std::endl ; 
    std::cout << std::boolalpha << 
     addIsDefined<unsigned int,unsigned int>::isDefined<std::numeric_limits<unsigned int>::max(),std::numeric_limits<unsigned int>::max()>() << std::endl ; 
} 

что приводит к (see it live):

true 
false 
true 

Не очевидно, что стандарт требует такого поведения, но, по-видимому, это comment by Howard Hinnant указывает на это действительно есть:

[...] and is also constexpr, meaning UB is caught at compile time

Update

Как-то я пропустил Issue 695 Compile-time calculation errors in constexpr functions, который вращается над формулировкой раздела 5 пункта , который используется, чтобы сказать (курсив мой идти вперед):

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression appears where an integral constant expression is required (5.19 [expr.const]), in which case the program is ill-formed.

и далее:

intended as an acceptable Standardese circumlocution for “evaluated at compile time,” a concept that is not directly defined by the Standard. It is not clear that this formulation adequately covers constexpr functions.

и позднее примечание говорит:

[...]There is a tension between wanting to diagnose errors at compile time versus not diagnosing errors that will not actually occur at runtime.[...]The consensus of the CWG was that an expression like 1/0 should simply be considered non-constant; any diagnostic would result from the use of the expression in a context requiring a constant expression.

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

Мы не можем однозначно сказать, что это было целью, но это действительно означает, что это так. Разница в том, как clang и gcc относятся к неопределенным сдвигам, оставляет некоторые сомнения.

Я подал заявку gcc bug report: Right and left shift undefined behavior not an error in a constexpr. Хотя похоже, что это соответствует, оно нарушает SFINAE, и из моего ответа на вопрос Is it a conforming compiler extension to treat non-constexpr standard library functions as constexpr? видно, что расхождение в реализации, наблюдаемое для пользователей SFINAE, представляется нежелательным для комитета.

+0

Спросите и ответьте на свой вопрос в ту же минуту? –

+21

@ ZacHowland, пожалуйста, см. [Могу ли я ответить на свои вопросы, даже если бы я знал ответ, прежде чем спрашивать?] (Http://meta.stackexchange.com/questions/17463/can-i-answer-my-own-questions- even-if-i-know-the-answer-before-ask) –

+2

К сожалению, вам кажется, что вам повезло с синергией, и на самом деле не является основанием для исключения неопределенного поведения из оценки 'constexpr' в первую очередь и, следовательно, Ответьте на вопрос, который вы задали. (никакого обоснования не предусмотрено в ДР) –

6

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

+0

Это хороший момент! –

+3

Я не думаю, что постоянное выражение * требуется * для оценки во время компиляции * во всех контекстах *. Для границ массива и т. Д. Ясно, что это «требуется» неявно, но я не уверен, есть ли общее требование. – dyp

+0

(Кроме того, я не думаю, что стандарт требует какого-либо поведения * компилятора *, только для * программы *.) – dyp

19

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

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

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

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

+2

+1 Благодарю вас, я надеялся получить ответ, который действительно вошел в обоснование. Я потратил много времени, пытаясь найти это обоснование прошлой ночью, но я не думаю, что есть что-то публичное, которое его охватывает. –

+5

К сожалению, я не знаю, является ли это обоснованием, рассматриваемым комитетами. Но именно поэтому я согласен с этим правилом. –

+0

Есть ли причина, по которой Стандарт не может/не должен быть написан, что если выражение используется в месте, требующем постоянной целочисленного целого числа, тогда даже в тех случаях, когда Стандарт не будет * иначе * налагать какие-либо требования относительно поведение этого выражения, компилятор должен * либо * определить поведение этого выражения, как получение целочисленного значения без побочных эффектов, или должен выдать диагностику? Так дано, например, 'int foo = -1 << 4;' компилятор, который определил '(-x) << y' в качестве эквивалента' - (x << y) ', мог бы без умолкнуть' int foo = -16; ', но где что сдвиг ловушки ... – supercat

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

  • Нет связанных вопросов^_^