Логическое AND + присваивание в c ++, безопасно? - PullRequest
10 голосов
/ 01 июля 2011

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

Чтобы объяснить шаблон, допустим, я представляю строку в виде связанного списка из них:

struct link_char;
struct link_char
{
   link_char * next;
   char code;
};

Обратите внимание, что последний символ любой строки link_char всегда будет иметь код == 0. Это свойство означает, что я могу проверить значение в строке, используя && короткое замыкание, чтобы предотвратить доступ по нулевому указателю.

bool equals_hello( const link_char * first_char )
{
    const link_char * c = first_char;

    return       c->code=='h' 
    && (c=c->next)->code=='e' 
    && (c=c->next)->code=='l' 
    && (c=c->next)->code=='l' // if string == "hel", we short-circuit here
    && (c=c->next)->code=='o';
}

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

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

// think of these as a bunch of HRESULT type functions 
//   a return value of 0 means SUCCESS
//   a return value of non-zero yields an Error Message
int err;
( !(err=initialize()) && !(err=create_window()) && !(err=run_app() )
    || handle_error(err);

Будут ли эти операции работать как запланированные кроссплатформенные? Я читал, что «если вы дважды читаете переменную в выражении, где вы также пишете, результат не определен». Но интуитивно я чувствую, что короткое замыкание гарантирует порядок, не так ли?

Ответы [ 2 ]

15 голосов
/ 01 июля 2011

Да.

Встроенное логическое И (&&), логическое ИЛИ (||) и оператор запятой (,) - это только * случаев, в которых для двоичного оператора C ++ гарантирует, что вычисление будет вычислять левое выражение, а затем (если не короткое замыкание) правое выражение (оператор запятой конечно всегда вычисляет оба операнда, сначала левый, а затем правый).

Обратите также внимание, что запятая между аргументами функции не является оператором запятой, и, следовательно, порядок вычисления аргументов функции не указан и даже хуже: например, в f(g(h()),i()) возможно, что последовательность вызовов будет h,i,g,f.

Также гарантия на порядок оценки распространяется только на встроенных операторов; если вы переопределите их, то они в основном станут вызовами функций, где порядок оценки аргументов не гарантирован и где короткое замыкание не выполняется.

Другие бинарные операторы не гарантируют порядок вычисления, и, например, одна распространенная ошибка - думать, что в:

std::cout << foo() << bar();

вызов на foo() гарантированно произойдет до вызова на bar() ... это не верно.

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

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

struct Foo
{
   int x, y;
   Foo() : y(compute_y()), x(compute_x()) {}
};

в этом случае гарантировано , что вызов compute_x() будет выполнен ДО вызова compute_y(), потому что x предшествует y в объявлениях членов.

3 голосов
/ 01 июля 2011

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

Встроенный оператор && имеет гарантированную оценку короткого замыкания, что означает, что он вводит точку последовательности: C ++ 98 §5.14 / 2 «Все побочные эффекты первого выражения, за исключением уничтожения временных (12.2 ) произойдет до того, как будет вычислено второе выражение ".

Так что проблем нет. C ++.

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

Приветствия & hth.,

...