Операция сцепления и порядок оценки на одном и том же объекте

Рассмотрим class MyClass с:

  • функция-член myClass& myFunction1(int) это изменяет
    объект и возврат *this
  • функция-член int myFunction2() const это не
    изменить объект

Гарантирует ли стандарт C ++ 11/14, что:

myclass.myFunction1(myclass.myFunction2()).myFunction1(myclass.myFunction2());

эквивалентно:

myclass.myFunction1(myclass.myFunction2());
myclass.myFunction1(myclass.myFunction2());

2

Решение

Нет. Компилятор может сначала вызвать myclass.myFunction2() дважды, а затем сделать два myFunction1 звонки в первом примере кода. Но не во втором примере кода.

Нет ничего, что мешало бы компилятору что-то застревать между оценкой аргументов функции и вызовом функции. Пока вызов действительно происходит после оценки аргументов вызова (этой функции). В стандартном выражении

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

Различные выражения, как правило, не упорядочены (если нет явной формулировки, которая их объединяет). Ваши два звонка myclass.myFunction2 такие непоследовательные случаи, так что один из призывов к myclass.myFunction2 может появиться после другого (и перед звонком на любой из myFunction1).

3

Другие решения

Порядок вычисления операндов любого оператора C ++, включая порядок вычисления аргументов функции в выражении вызова функции и порядок вычисления подвыражений в любом выражении, не определен (кроме случаев, отмеченных ниже). Компилятор оценит их в любом порядке и может выбрать другой порядок при повторном вычислении того же выражения.
В C ++ не существует понятия оценки слева направо или справа налево, что не следует путать с ассоциативностью операторов слева направо и справа налево: выражение f1 () + f2 ( ) + f3 () анализируется как (f1 () + f2 ()) + f3 () из-за ассоциативности слева направо оператора +, но вызов функции для f3 может оцениваться первым, последним или между f1 () или f2 () во время выполнения.

как указано Вот для всех, кто пытался написать выражение в польской нотации и применить оператор c ++ старшинство правила

1

Как отмечали другие, призывы к myFunction2() неупорядочены относительно друг друга. Так что это не то же самое.

В качестве попытки упорядочить их мы могли бы сделать что-то вроде этого:

template<typename T>
struct continuation {
T&& t;
continuation(T&& t_):t(std::forward<T>(t_)) {}
continuation( continuation&& ) = delete;
continuation( continuation const& ) = delete;
continuation() = delete;
void operator=(continuation const&) = delete;
void operator=(continuation &&) = delete;
template<typename Lambda>
continuation<T> then( Lambda&& closure ) && {
std::forward<Lambda>(closure)( std::forward<T>(t) );
return { std::forward<T>(t) }; // two forwards!  Dangerous
}
};
struct myClass {
continuation<myClass&> myFunction1(int) &{ return {*this}; }
continuation<myClass> myFunction1(int) &&{ return {*this}; }
int myFunction2() const { return 7; }
};
int main() {
myClass c;
c.myFunction1( c.myFunction2() ).then( [](auto&& c) { c.myFunction1( c.myFunction2() ); } );
}

будет примером кода, который позволит вам использовать экземпляр класса в одну строку с одинаковым порядком операций (в C ++ 1y).

Тем не менее, продолжение стиля then вызовы традиционно используются для будущих возвращаемых значений, а не для упорядоченной оценки цепных вызовов this,

Это также становится действительно раздражающим со всеми вложенными скобками. Один из способов подойти тот проблема заключается в использовании именованного оператора *then*, который требует немного больше шаблонного, но дает вам:

int main() {
myClass c;
c.myFunction1( c.myFunction2() )
*then* [](auto&& c) { c.myFunction1( c.myFunction2() ); }
*then* [](auto&& c) { c.myFunction1( c.myFunction2() ); };
}

что выглядит довольно мило.

Все это в основном способ избежать создания простого псевдонима.

myClass get_my_class() { return {} };
int main() {
auto&& c = get_my_class();
c.myFunction1(c.myFunction2());
c.myFunction1(c.myFunction2());
c.myFunction1(c.myFunction2());
}

и то, и другое не требует никакой гимнастики выше, и, по крайней мере, так же эффективно. Если у вас есть сложное имя для какого-либо объекта, и вы хотите избежать повторения этого слова, использование ссылки работает во всех случаях, кроме некоторых угловых.

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

Однако может возникнуть ситуация, когда это неизбежно. Вместо создания цепочки или продолжения метода или чего-либо еще, рассмотрите возможность создания простой лямбда-функции или функции, которая объединяет операции в цепочку, и вызывайте ее в одной строке, в которой существует объект. В качестве альтернативы, разбейте строку и сохраните время жизни объекта через расширение ссылки его первоначального воплощения.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector