С С++ 11 это поведение undefined для записи f (x ++), g (x ++)?
Я читал этот вопрос:
Undefined поведение и точки последовательности
и, в частности, С++ 11, и я понимаю идею "упорядочивания" оценок. Но - есть ли достаточная последовательность, когда я пишу:
f(x++), g(x++);
?
То есть, я уверен, что f()
получает исходное значение x
, а g()
получает однократный x
?
Примечания для nitpickers:
- Предположим, что
operator++()
определил поведение (даже если мы его переопределили), а также f()
и g()
, что никакие исключения не будут выбрасываться и т.д. - этот вопрос не об этом.
- Предположим, что
operator,()
не было перегружено.
Ответы
Ответ 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++));