Рассмотрим 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());
Нет. Компилятор может сначала вызвать myclass.myFunction2()
дважды, а затем сделать два myFunction1
звонки в первом примере кода. Но не во втором примере кода.
Нет ничего, что мешало бы компилятору что-то застревать между оценкой аргументов функции и вызовом функции. Пока вызов действительно происходит после оценки аргументов вызова (этой функции). В стандартном выражении
При вызове функции (независимо от того, является ли функция встроенной), каждое вычисление значения и побочный эффект, связанный с любым выражением аргумента или с выражением постфикса, обозначающим вызываемую функцию, упорядочивается перед выполнением каждого выражения или оператора в теле вызываемая функция.
Различные выражения, как правило, не упорядочены (если нет явной формулировки, которая их объединяет). Ваши два звонка myclass.myFunction2
такие непоследовательные случаи, так что один из призывов к myclass.myFunction2
может появиться после другого (и перед звонком на любой из myFunction1
).
Порядок вычисления операндов любого оператора C ++, включая порядок вычисления аргументов функции в выражении вызова функции и порядок вычисления подвыражений в любом выражении, не определен (кроме случаев, отмеченных ниже). Компилятор оценит их в любом порядке и может выбрать другой порядок при повторном вычислении того же выражения.
В C ++ не существует понятия оценки слева направо или справа налево, что не следует путать с ассоциативностью операторов слева направо и справа налево: выражение f1 () + f2 ( ) + f3 () анализируется как (f1 () + f2 ()) + f3 () из-за ассоциативности слева направо оператора +, но вызов функции для f3 может оцениваться первым, последним или между f1 () или f2 () во время выполнения.
как указано Вот для всех, кто пытался написать выражение в польской нотации и применить оператор c ++ старшинство правила
Как отмечали другие, призывы к 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());
}
и то, и другое не требует никакой гимнастики выше, и, по крайней мере, так же эффективно. Если у вас есть сложное имя для какого-либо объекта, и вы хотите избежать повторения этого слова, использование ссылки работает во всех случаях, кроме некоторых угловых.
В частности, угловой случай — это функция, которая возвращает ссылку на объект, время жизни которого заканчивается в конце текущей строки. Как правило, это плохая практика: по крайней мере, с легко перемещаемыми объектами вы должны вместо этого вернуть временный.
Однако может возникнуть ситуация, когда это неизбежно. Вместо создания цепочки или продолжения метода или чего-либо еще, рассмотрите возможность создания простой лямбда-функции или функции, которая объединяет операции в цепочку, и вызывайте ее в одной строке, в которой существует объект. В качестве альтернативы, разбейте строку и сохраните время жизни объекта через расширение ссылки его первоначального воплощения.