Каковы гарантии порядка оценки, введенные С++ 17?

Каковы последствия проголосовавших в С++ 17 гарантий порядка оценки (P0145R3) на типичном С++-коде? (изменить: обновила ссылку на последнюю версию, r3)

Что изменилось в таких вещах, как

i=1;
f(i++, i)

и

std::cout << f() << f() << f() ;

или

f(g(),h(),j());
39
задан Johan Lundberg 21 июля '16 в 13:21
источник поделиться
2 ответов

Некоторые распространенные случаи, когда оценочный порядок до сих пор не указан, указаны и действительны с помощью C++17. Некоторое поведение undefined теперь не указано.

Как насчет таких вещей, как

i=1;
f(i++, i)

был undefined, но теперь не указан.

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

Пример происходит из языка программирования С++, четвертого издания, Stroustrup и используется для неопределенного поведения, но с С++ 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() ;
}

С С++ 14 и прежде чем мы сможем (и будем) получать результаты, такие как

play
no,and,Work,All,

вместо

All,work,and,no,play

Обратите внимание, что вышеописанное действует так же, как

(((((std::cout << spk()) << spk()) << spk()) << spk()) << spk()) ;

Но все же, перед С++ 17 не было гарантии, что первые вызовы придут первым в поток.

Ссылки: из принятое предложение:

Постфиксные выражения оцениваются слева направо. Это включает вызовы функций и выражения выбора членов.

Выражения присваивания оцениваются справа налево. Эта включает составные присвоения.

Операнды для переключения операторов вычисляются слева направо. В резюме, следующие выражения оцениваются в порядке а, то b, то c, то d:

  • a.b
  • а- > б
  • а → * б
  • a (b1, b2, b3)
  • b @= a
  • а [Ь]
  • a < б
  • a → b

Кроме того, мы предлагаем следующее дополнительное правило: порядок оценка выражения с использованием перегруженного оператора определяемый порядком, связанным с соответствующим встроенным оператора, а не правила для вызовов функций.

Изменить примечание: Мой первоначальный ответ неверно истолкован a(b1, b2, b3). Порядок b1, b2, b3 пока не определен. (спасибо @KABoissonneault, все комментаторы.)

Однако (как указывает @Yakk), и это важно: даже когда b1, b2, b3 являются нетривиальными выражениями, каждая из них полностью оценивается и привязывается к соответствующему параметру функции до другие начинают оцениваться. Стандарт гласит следующее:

§5.2.2 - вызов функции 5.2.2.4:

., Постфикс-выражение секвенируется перед каждым выражением в список выражений и любой аргумент по умолчанию. Каждое вычисление стоимости и побочный эффект, связанный с инициализацией параметра, и инициализация, секвенируется перед вычислением каждого значения и побочный эффект, связанный с инициализацией любого последующего Параметр.

Однако одно из этих новых предложений отсутствует в github draft:

Каждое вычисление значения и побочный эффект, связанный с инициализация параметра и сама инициализация, является секвенированы перед каждым вычислением значения и связанным с ним побочным эффектом с инициализацией любого последующего параметра.

Вот пример. Он решает многолетние проблемы (Как объясняется Herb Sutter) с исключительной безопасностью, где такие вещи, как

f(std::unique_ptr<A> a, std::unique_ptr<B> b);

f(get_raw_a(),get_raw_a()); 

будет просачиваться, если один из вызовов get_raw_a() будет бросать перед другим raw указатель был привязан к этому параметру умного указателя. edit: как указано T.C. этот пример ошибочен, так как unique_ptr-конструирование из raw-указателя является явным, что предотвращает его компиляцию.

Также обратите внимание на этот классический question (с тегом C, а не на С++):

int x=0;
x++ + ++x;

по-прежнему undefined.

32
ответ дан Johan Lundberg 21 июля '16 в 13:22
источник поделиться

Перемежение запрещено в С++ 17

В С++ 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), что предотвратило бы утечку.


В С++ 17 новые правила запрещают чередование. Из [intro.execution]:

Для каждого вызова функции F для каждой оценки A, которая встречается внутри F и каждой оценки B, которая не встречается внутри F, но оценивается в том же потоке и как часть одного и того же обработчика сигнала (если есть), либо A является секвенированы до того, как B или B секвенированы до A.

Есть сноска к этому предложению, которое гласит:

Другими словами, выполнение функций не чередуется друг с другом.

Это оставляет нам два действительных порядка: (1), (2), (3), (4) или (3), (4), (1), (2). Не указано, какой заказ сделан, но оба они безопасны. Все порядки, в которых (1) (3) оба случаются до (2) и (4), теперь запрещены.

9
ответ дан Barry 28 сент. '17 в 18:05
источник поделиться

Другие вопросы по меткам или Задайте вопрос