14 Ocak 2020 Salı

Order Of Evaluation

1. Metod Çağrısı
Metoda geçilen parametrelerin hangi sırada işleneceğinin sırası belirsizdir.
C++ does not define the order in which function parameters are evaluated.
Dolayısıyla metodun sonucu da tanımsızdır. Derleyiciden derleyiciye değişebilir.
Örnek
Şöyle yapmamalıyız.
f(i = 1, i = -1);
Örnek
Bir başka örnek
int nValue = Add(x, ++x);
Örnek
Şimdiye kadar verilen örneklerde metodu biz yazdık. Global +operator metodunda da aynı problem görülebilir. Örnek:
std::cout << stream.readLine() + "\n" + stream.readLine() << std::endl;
Global + operatörü de parametreyi yani readLine() metodunu istediği sırada çağırabilir. Bu durumda çıktı olarak
line1
line2
yerine
line2
line1
alabiliriz.
Örnek
Aynı problem << operatöründe de görülebilir.
int f() {
  static int i = 0;
  return i++;
}

int main() {
  cout << f() << " " << f() << " " << f() << endl ;
  return 0;
}
Çıktı olarak  2 1 0 alırız.

2.Metod Interleaving
Örnek
Elimizde şöyle bir kod olsun.
void foo(std::unique_ptr<A>, std::unique_ptr<B> );

foo(std::unique_ptr<A>(new A), std::unique_ptr<B>(new B));
Bu çağrılara şöyle rakamlar verelim
1. new A
2. unique_ptr<A> constructor
3. new B
4. unique_ptr<B> constructor

C++17'ye kadar method interleaving olabiliyordu. Dolayısıyla önce 1, 3 , 2, ,4 çağrılabiliyordu. C++17 ile ya 1,2 ve 3,4 veya 3,4 ve 1,2 olabilir.
Örnek
Açıklaması şöyle.
code such as f(std::shared_ptr<int>(new int(42)), g()) can cause a memory leak if g gets called after new int(42) and throws an exception, while f(std::make_shared<int>(42), g()) is safe, since two function calls are never interleaved.
3.Aynı parametrenin birden fazla değer değiştirerek kullanılması
The C++ language says you cannot modify a variable more than once between sequence points
Örnek - scalar
Şöyle yapmamalıyız.
a[i] = i++;
Açıklaması şöyle.
If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent (1.10), the behavior is undefined.
Örnek - function call
Aynı parametreyi değiştirerek şöyle yapmamalıyız
msg[ipos++] = checksum(&msg[1], ipos-1);
Örnek - function call
Elimizde şöyle bir kod olsun. Bu kod için order of evaluation belirsiz olduğu için unspecified davranışa sabep olur.
static int global_var = 0;

int update_three(int val)
{
  global_var = val;
  return 3;
}

main() {
  int arr[5];
  arr[global_var] = update_three(2);
}
Açıklaması şöyle
Thus, the C implementation may evaluate arr[global_var] first and then do the function call, in which case there is a sequence point between them because there is one before the function call, or it may evaluate global_var = val; in the function call and then arr[global_var], in which case there is a sequence point between them because there is one after the full expression. So the behavior is unspecified—either of those two things may be evaluated first—but it is not undefined.
Örnek - iterator
Şöyle yapmamalıyız. Iterator raw pointer döndürüyor olabilir.
foo f;
auto it = f.begin();
*it = *it++; // <<== This is UB
Örnek - integral
Şöyle yapmamalıyız
x = ++y + y++
Aslında standart tam olarak şunu söylüyor. Yani derleyici operand'ları istediği sırada çağırır, ve eğer bu çağrıların yan etkisi (side effect) varsa davranış tanımsızdır diyor.
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.
3.Macrolar
Açıklaması şöyle
Because increment and decrement operators have side effects, using expressions
with increment or decrement operators in a preprocessor macro can have undesirable results
Şöyle yapmamalıyız.
#define max(a,b) ((a)<(b))?(b):(a)
 k = max( ++i, j );

Hiç yorum yok:

Yorum Gönder