Подтвердить что ты не робот

С С++ 11 это поведение undefined для записи f (x ++), g (x ++)?

Я читал этот вопрос:

Undefined поведение и точки последовательности

и, в частности, С++ 11, и я понимаю идею "упорядочивания" оценок. Но - есть ли достаточная последовательность, когда я пишу:

f(x++), g(x++);?

То есть, я уверен, что f() получает исходное значение x, а g() получает однократный x?

Примечания для nitpickers:

  • Предположим, что operator++() определил поведение (даже если мы его переопределили), а также f() и g(), что никакие исключения не будут выбрасываться и т.д. - этот вопрос не об этом.
  • Предположим, что operator,() не было перегружено.
4b9b3361

Ответ 1

Нет, это не поведение undefined.

В соответствии с этот порядок оценки и последовательность действий левая часть запятой полностью оценивается перед правой стороной (см. rule 9):

9) Каждое вычисление значения и побочный эффект первого (левого) аргумента встроенного оператора запятой секвенируется перед каждым вычислением значения и побочным эффектом второго (правого) аргумента.

Это означает, что выражение типа f(x++), g(x++) не undefined.

Обратите внимание, что это допустимо только для встроенного оператора запятой.

Ответ 2

Чтобы процитировать С++ 11 (n3337) [expr.comma/1]:

Пара выражений, разделенных запятой, оценивается слева направо; левое выражение является выражением отбрасываемого значения (раздел [expr]). Каждое вычисление значения и побочный эффект, связанный с левым выражение секвенируется перед вычислением каждого значения и побочным эффектом связанный с правильным выражением.

И я беру "каждый", чтобы означать "каждый" 1. Оценка второй x++ не может произойти до завершения последовательности вызовов до f и возвращает f. 2


<суб > 1 Деструкторные вызовы не связаны с подвыражениями, только с полными выражениями. Таким образом, вы увидите, что они выполнены в обратном порядке для создания временного объекта в конце полного выражения.
2 Этот параграф применяется только к запятой при использовании в качестве оператора. Когда запятая имеет особое значение (например, при назначении последовательности аргументов вызова функции), это не применяется. Суб >

Ответ 3

Это зависит.

Сначала предположим, что x++ сам по себе не вызывает поведение undefined. Подумайте о подписанном переполнении, увеличении указателя прошлого и конца, или оператор postfix-increment может быть определен пользователем).
Кроме того, предположим, что вызов f() и g() с их аргументами и уничтожение временных файлов не вызывает поведение undefined.
Это довольно много предположений, но если они сломаны, ответ тривиален.

Теперь, если запятая является встроенным запятой, запятая в скобках-init-list или запятая в списке mem-initializer, левая и правая стороны секвенированы либо до, либо после каждого другие (и вы знаете, что), поэтому не мешайте, делая поведение корректным.

struct X {
    int f, g;
    explicit X(int x) : f(x++), g(x++) {}
};
// Demonstrate that the order depends on member-order, not initializer-order:
struct Y {
    int g, f;
    explicit Y(int x) : f(x++), g(x++) {}
};
int y[] = { f(x++), g(x++) };

В противном случае, если x++ вызывает пользовательскую перегрузку оператора для постфиксного приращения, у вас есть неопределенное упорядочение двух экземпляров x++ и, следовательно, неопределенное поведение.

std::list<int> list{1,2,3,4,5,6,7};
auto x = begin(list);
using T = decltype(x);

void h(T, T);
h(f(x++), g(x++));
struct X {
    X(T, T) {}
}
X(f(x++), g(x++));

И в последнем случае вы получаете полномасштабное поведение undefined, так как два постфиксальных приращения x не подвержены влиянию.

int x = 0;

void h(int, int);
h(f(x++), g(x++));
struct X {
    X(int, int) {}
}
X(f(x++), g(x++));