Порядок вычисления параметров функции С++

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

a(b(),c());

тогда поведение этого может быть неопределенным в <= С++ 14 и неопределенным в> = С++ 17 в том смысле, что компилятору решать, следует ли сначала вычислять b или 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);
}

Я нахожусь в неопределенном поведении земли? Или это, как предполагается, "заставить" порядок оценки?

Ответ 1

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

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

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

Ответ 2

Вот еще один способ форсировать порядок оценки, используя 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()});

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