Порядок вычисления параметров функции C ++ - PullRequest
21 голосов
/ 24 мая 2019

Я понимаю, что когда я вызываю такую ​​функцию, как

a(b(),c());

, поведение этого может быть неопределенным в <= C ++ 14 и неопределенным в> = C ++ 17 в том смысле,что компилятор должен определить, нужно ли сначала b или c оценивать.

Я хотел бы знать, как лучше навести порядок оценки.Я буду компилировать как C ++ 14.

То, что сразу приходит на ум, примерно так:

#include <iostream>

int count = 5;
auto increment(){
    return count++;
}

template <typename A, typename B>
auto diff(A && a, B && b){
   return a - b;
}

int main() {
    auto && a = increment();
    auto && b = increment();
    auto c = diff(a,b);
}

Я в неопределенном поведении, приземляюсь?Или это, как предполагается, «заставить» порядок оценки?

Ответы [ 2 ]

28 голосов
/ 24 мая 2019

Точка с запятой, которая разделяет операторы, налагает отношение «происходит раньше». auto && a = increment() должен быть оценен первым. Это гарантировано. Возвращенный временный объект будет привязан к ссылке a (и его время жизни увеличено) перед вторым вызовом increment.

Там нет UB. Это способ форсировать порядок оценки.

Единственное, что здесь нужно, это то, что если increment вернул саму ссылку, то вам нужно было бы беспокоиться о проблемах жизни. Но если бы не было проблем с продолжительностью жизни, скажем, если бы он возвращал ссылку на count, все равно не было бы UB из навязанной оценки a, а затем b.

16 голосов
/ 24 мая 2019

Вот еще один способ форсировать порядок оценки, используя std::initializer_list, который имеет гарантированный порядок слева направо:

#include <numeric> // for accumulate
#include <initializer_list>

template <class T>
auto diff(std::initializer_list<T> args)
{
   return std::accumulate(args.begin(), args.end(), T(0), std::minus<>{});
}

const auto result = diff({increment(), increment()});

Это ограничивает вас объектами одного типа, и вам необходимо ввести дополнительные скобки.

...