Rozważ ten temat jako kontynuację następującego tematu:
Poprzednia rata
Niezdefiniowane zachowanie i punkty sekwencji
Wróćmy do tego zabawnego i zawiłego wyrażenia (wyrażenia zapisane kursywą pochodzą z powyższego tematu * uśmiech *):
i += ++i;
Mówimy, że wywołuje to niezdefiniowane zachowanie. Przypuszczam, że kiedy powiem, możemy założyć, że niejawnie typu o i
to jeden z wbudowanych typów.
Co zrobić, jeśli typ o i
to typ zdefiniowany przez użytkownika? Powiedz, że jego typ jest Index
zdefiniowany w dalszej części tego postu (patrz poniżej). Czy nadal wywoływałoby niezdefiniowane zachowanie?
Jeśli tak, dlaczego? Czy nie jest to równoznaczne z pisaniem i.operator+=(i.operator++());
lub nawet prostsze składniowo i.add(i.inc());
? A może one również wywołują niezdefiniowane zachowanie?
Jeśli nie, dlaczego nie? W końcu obiekt i
jest modyfikowany dwukrotnie między kolejnymi punktami sekwencji. Przypomnij sobie praktyczną zasadę: wyrażenie może modyfikować wartość obiektu tylko raz między kolejnymi "punktami sekwencji . A jeśli i += ++i
jest wyrażeniem, to musi wywołać niezdefiniowane zachowanie. Jeśli tak, to jego odpowiedniki, i.operator+=(i.operator++());
a i.add(i.inc());
także musi wywołać niezdefiniowane zachowanie, które wydaje się nieprawdziwe! (o ile rozumiem)
A i += ++i
może nie jest to wyrażenie na początek? Jeśli tak, to co to jest i jaka jest definicja wyrażenia ?
Jeśli jest to wyrażenie, a jednocześnie jego zachowanie jest również dobrze zdefiniowane, oznacza to, że liczba punktów sekwencji powiązanych z wyrażeniem zależy w jakiś sposób od typu operandów zaangażowanych w wyrażenie. Czy mam rację (choćby częściowo)?
A propos, co powiesz na to wyrażenie?
//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the subscript operator!
a[++i] = i; //Taken from the previous topic. But here type of `i` is Index.
Musisz to również wziąć pod uwagę w swojej odpowiedzi (jeśli na pewno znasz jej zachowanie). :-)
Jest
++++++i;
dobrze zdefiniowane w C ++ 03? W końcu to jest to,
((i.operator++()).operator++()).operator++();
class Index
{
int state;
public:
Index(int s) : state(s) {}
Index& operator++()
{
state++;
return *this;
}
Index& operator+=(const Index & index)
{
state+= index.state;
return *this;
}
operator int()
{
return state;
}
Index & add(const Index & index)
{
state += index.state;
return *this;
}
Index & inc()
{
state++;
return *this;
}
};
s
jest typem zdefiniowanym przez użytkownika!)Odpowiedzi:
Wygląda na kod
i.operator+=(i.operator ++());
Działa doskonale w odniesieniu do punktów sekwencji. Sekcja 1.9.17 normy C ++ ISO mówi tak o punktach sekwencji i ocenie funkcji:
Oznaczałoby to na przykład, że
i.operator ++()
parametr asoperator +=
ma punkt sekwencji po dokonaniu oceny. Krótko mówiąc, ponieważ przeciążone operatory są funkcjami, obowiązują normalne zasady sekwencjonowania.Swoją drogą, świetne pytanie! Naprawdę podoba mi się sposób, w jaki zmuszasz mnie do zrozumienia wszystkich niuansów języka, o którym już myślałem, że znam (i myślałem, że myślałem, że znam). :-)
źródło
http://www.eelis.net/C++/analogliterals.xhtml Przychodzi mi do głowy analogowe literały
unsigned int c = ( o-----o | ! ! ! ! ! o-----o ).area; assert( c == (I-----I) * (I-------I) ); assert( ( o-----o | ! ! ! ! ! ! ! o-----o ).area == ( o---------o | ! ! ! o---------o ).area );
źródło
Jak powiedzieli inni, twój
i += ++i
przykład działa z typem zdefiniowanym przez użytkownika, ponieważ wywołujesz funkcje, a funkcje zawierają punkty sekwencji.Z drugiej strony
a[++i] = i
nie ma tyle szczęścia, zakładając, żea
jest to podstawowy typ tablicy, a nawet typ zdefiniowany przez użytkownika. Problem polega na tym, że nie wiemy, która część wyrażenia zawierającegoi
jest oceniana jako pierwsza. Może się zdarzyć, że++i
zostanie oszacowana, przekazana dooperator[]
(lub wersja surowa) w celu pobrania tam obiektu, a następnie wartośći
zostanie przekazana do tego (czyli po zwiększeniu i). Z drugiej strony, być może druga strona jest najpierw oceniana, przechowywana do późniejszego przypisania, a następnie++i
część jest oceniana.źródło
++i
a odczytem zi
prawej strony przypisania, kolejność byłaby nieokreślona. Ponieważ jedna z dopuszczalnych kolejności robi te dwie rzeczy bez interweniującego punktu sekwencji, zachowanie jest nieokreślone.a
i wbudowanymi
.Myślę, że to dobrze zdefiniowane:
Z wersji roboczej standardu C ++ (n1905) § 1.9 / 16:
Zwróć uwagę na część, którą pogrubiłem. Oznacza to, że rzeczywiście istnieje punkt sekwencji po wywołaniu funkcji inkrementacji (
i.operator ++()
), ale przed wywołaniem przypisania złożonego (i.operator+=
).źródło
W porządku. Po przejrzeniu poprzednich odpowiedzi, ponownie przemyślałem własne pytanie, szczególnie tę część, na którą tylko Noah próbował odpowiedzieć, ale nie jestem do niego całkowicie przekonany.
Przypadek 1:
Jeśli
a
jest tablicą typu wbudowanego. Zatem to, co powiedział Noe, jest poprawne. To jest,Tak więc
a[++i]=i
wywołuje niezdefiniowane zachowanie lub wynik jest nieokreślony. Cokolwiek to jest, nie jest dobrze zdefiniowane!PS: W powyższym cytacie,
przekreśleniejest oczywiście mój.Przypadek 2:
Jeśli
a
jest obiektem typu zdefiniowanego przez użytkownika, który przeciąża elementoperator[]
, to znowu istnieją dwa przypadki.operator[]
funkcję jest typem wbudowanym, wówczas ponowniea[++i]=i
wywołuje niezdefiniowane zachowanie lub wynik jest nieokreślony.operator[]
funkcję jest typem zdefiniowanym przez użytkownika, to zachowaniea[++i] = i
jest dobrze zdefiniowane (o ile rozumiem), ponieważ w tym przypadkua[++i]=i
jest równoważne pisaniu,a.operator[](++i).operator=(i);
które jest takie samo jaka[++i].operator=(i);
,. Oznacza to, że przypisanieoperator=
zostaje wywołane na zwrócony obiekt ofa[++i]
, który wydaje się być bardzo dobrze zdefiniowany, ponieważ do czasua[++i]
powrotu++i
zostały już ocenione, a następnie zwrócony obiekt wywołujeoperator=
funkcję przekazującą jej zaktualizowaną wartośći
jako argument. Zwróć uwagę, że między tymi dwoma wywołaniami istnieje punkt sekwencji . A składnia zapewnia, że nie ma konkurencji między tymi dwoma wywołaniami, aoperator[]
byłby wywoływany jako pierwszy, a następnie argument++i
przekazany do niego również zostałby oceniony jako pierwszy.Pomyśl o tym, jak o tym, że
someInstance.Fun(++k).Gun(10).Sun(k).Tun();
każde kolejne wywołanie funkcji zwraca obiekt pewnego typu zdefiniowanego przez użytkownika. Dla mnie ta sytuacja wygląda bardziej tak:eat(++k);drink(10);sleep(k)
ponieważ w obu sytuacjach istnieje punkt sekwencji po każdym wywołaniu funkcji.Proszę popraw mnie jeżeli się mylę. :-)
źródło
k++
i niek
są oddzielone punktami sekwencji. Oba mogą być ocenione przed jedną lub z nich. Język wymaga tylko, aby był oceniany wcześniej , a nie argumenty tego są oceniane przed argumentami. W pewnym sensie wyjaśniam to samo ponownie, nie będąc w stanie podać odniesienia, więc nie będziemy robić postępów w tym miejscu.Sun
Fun
Fun
Sun
Fun
Sun
Sun
wykonaniu, aleFun
argument++k
może zostać oceniony przed lub po nim. Istnieją punkty sekwencji przed i poFun
wykonaniu, aleSun
argumentk
może zostać oceniony przed lub po nim. Dlatego jednym możliwym przypadkiem jest to, że obak
i++k
są oceniane przed alboSun
lubFun
są oceniane, a więc oba znajdują się przed punktami sekwencji wywołania funkcji, więc nie ma oddzielającego punktu sekwencjik
i++k
.eat(i++);drink(10);sleep(i);
? ... nawet teraz, można powiedzieć, żei++
może zostać ocenione przed czy po tym?k
a++k
. W przykładzie jedzenia / picia, nie jest jako punkt sekwencji pomiędzyi
ii++
.eat()
isleep()
istnieje punkt (y) sekwencji, ale między nimi nie ma nawet jednego. W jaki sposób argumenty dwóch wywołań funkcji oddzielone punktami sekwencji mogą należeć do tych samych punktów sekwencji?