Каковы гарантии порядка оценки, введенные C++17?
каковы последствия голосования в C++17 гарантии порядка оценки (P0145) на типичном коде C++?
что это меняет в таких вещах, как
i=1;
f(i++, i)
и
std::cout << f() << f() << f() ;
или
f(g(),h(),j());
2 ответа:
некоторые типичные случаи, когда порядок оценки до сих пор нет данных, указаны и действительны с
C++17
. Некоторое неопределенное поведение теперь не определено.а как насчет таких вещей, как
i=1; f(i++, i)
был неопределенным,но теперь не определен.
std::cout << f() << f() << f() ;
было не указано, но станет совместимым с приоритетом оператора, так что первая оценка
f
придут первый в потоке. (примеры ниже).f(g(),h(),j());
все еще имеет неопределенный порядок оценки g, h, j. обратите внимание, что для
getf()(g(),h(),j())
, правила гласят, чтоgetf()
будет оцениваться передg,h,j
.Также обратите внимание на следующий пример из текста предложение:
std::string s = "but I have heard it works even if you don't believe in it" s.replace(0, 4, "").replace(s.find("even"), 4, "only") .replace(s.find(" don't"), 6, "");
пример исходит от язык программирования C++, 4-е издание, Stroustrup, и раньше было неопределенное поведение, но с В C++17 будет работать, как ожидалось. Были аналогичные проблемы с возобновляемыми функциями (
.then( . . . )
).в качестве другого примера, рассмотрим следующее:
#include <iostream> #include <string> #include <vector> #include <cassert> struct Speaker{ int i =0; Speaker(std::vector<std::string> words) :words(words) {} std::vector<std::string> words; std::string operator()(){ assert(words.size()>0); if(i==words.size()) i=0; // pre- C++17 version: auto word = words[i] + (i+1==words.size()?"\n":","); ++i; return word; // Still not possible with C++17: // return words[i++] + (i==words.size()?"\n":","); } }; int main() { auto spk = Speaker{{"All", "Work", "and", "no", "play"}}; std::cout << spk() << spk() << spk() << spk() << spk() ; }
С C++14 и до того, как мы можем (и будем) получать такие результаты, как
play no,and,Work,All,
вместо
All,work,and,no,play
обратите внимание, что вышеизложенное фактически совпадает с
(((((std::cout << spk()) << spk()) << spk()) << spk()) << spk()) ;
но все же, до C++17 не было никакой гарантии, что первые вызовы придут в первую очередь поток.
ссылки: с принял предложение:
Постфиксные выражения вычисляются слева направо. Это включает в себя вызовы функций и выражения выбора элемента.
выражения присваивания вычисляются справа налево. Этот включает в себя составные задания.
операнды для операторов сдвига вычисляются слева направо. В сводка, следующие выражения вычисляются в для заказа, затем b, затем c, затем d:
- а.б
- a - >b
- a - >*b
- a (b1, b2, b3)
- b @= a
- a[b]
- a
- a > > b
кроме того, мы предлагаем следующее дополнительное правило: порядок вычисление выражения с участием перегруженного оператора является определяется порядок, связанный с соответствующим встроенный оператор, а не правила для вызовов функций.
редактировать Примечание: мой оригинальный ответ неверно истолкован
a(b1, b2, b3)
. Порядокb1
,b2
,b3
пока не уточняется. (спасибо @KABoissonneault, все комментаторы.)однако, (как указывает @Yakk) и это важно: даже когда
b1
,b2
,b3
являются нетривиальными выражениями, каждое из которых полностью вычисляется и привязан к соответствующей функции параметр перед тем, как другие начинают оцениваться. В стандарте говорится следующее:§5.2.2 - вызов функции 5.2.2.4:
. . . Постфиксное-выражение применяется перед каждым выражением в выражение-список и любой аргумент по умолчанию. Каждое вычисление значения и побочный эффект, связанный с инициализацией параметра, и сама инициализация, секвенируется перед каждым вычислением значения и сторона эффект, связанный с инициализацией любого последующего параметр.
однако одно из этих новых предложений отсутствует в проект github:
каждое вычисление значения и побочный эффект связанные с инициализация параметра и сама инициализация-это секвенирован перед каждым вычислением значения и побочный эффект, связанный с инициализацией любого последующего параметр.
пример и там. Он решает десятилетние проблемы (как объяснил Херб Саттер) за исключением безопасности, где такие вещи, как
f(std::unique_ptr<A> a, std::unique_ptr<B> b); f(get_raw_a(),get_raw_a());
будет утечка, если один из вызовов
get_raw_a()
бросил бы перед другим необработанный указатель был привязан к его параметру смарт-указателя. edit: как указано T. C. пример является ошибочным, поскольку конструкция unique_ptr из необработанного указателя явна, предотвращая это скомпилировать.также обратите внимание на этот классический вопрос (с тегом C, а не C++):
int x=0; x++ + ++x;
по-прежнему не определено.
чередование запрещено в C++17
в C++14 небезопасно следующее:
void foo(std::unique_ptr<A>, std::unique_ptr<B> ); foo(std::unique_ptr<A>(new A), std::unique_ptr<B>(new B));
есть четыре операции, которые происходят здесь во время вызова функции
new A
unique_ptr<A>
конструкторnew B
unique_ptr<B>
конструкторпорядок их был совершенно не определен, и поэтому совершенно правильный порядок (1), (3), (2), (4). Если этот заказ был выбрано и (3) бросает, то память из (1) утечек - мы еще не запустили (2), что бы предотвратить утечку.
в C++17, новые правила запрещают чередование. Из [вступление.исполнение]:
для каждого вызова функции F, для каждого вычисления A, которое происходит в пределах F, и каждого вычисления B, которое не происходит в пределах F, но оценивается в том же потоке и как часть того же обработчика сигнала (если таковой имеется), либо a секвенируется перед B, либо B является секвенирован до А.
к этому предложению есть сноска, которая гласит:
другими словами, выполнение функций не чередуются друг с другом.
это оставляет нас с двумя допустимыми порядками: (1), (2), (3), (4) или (3), (4), (1), (2). Неизвестно, какой заказ принимается, но оба они безопасны. Все заказы, где (1) (3) оба происходят до (2) и (4) теперь запрещены.