Логическое И + назначение в С++, безопасно?

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

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

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

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

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);

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

Ответ 1

Да.

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

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