Dzisiejszy temat dotyczy jednego z najbardziej zdumiewających problemów poprzednich standarów języka C++. Dlaczego zdumiewających ? Między innymi dlatego, że udało mu się przetrwać dzielnie kilkanaście lat (!), pomimo swojej nieskomplikowanej natury – chodzi o podwójne nawiasy kątowe.
Ale jak to się stało ?
Spójrzmy na poniższy kod:
#include <vector> int main() { std::vector<std::vector<int>> myVector; return 0; }
Widzimy tutaj deklarację zagnieżdżonego kontenera typu std::vector. Kod wygląda poprawnie, ale wynik kompilacji przedstawia się mniej więcej tak (kompilator bez wsparcia dla C++11):
In function 'int main()': 5:32: error: '>>' should be '> >' within a nested template argument list
Błąd. Z komunikatu dowiadujemy się dokładnie, co powinniśmy zrobić, żeby wyeliminować błąd (dodanie znaku białego pomiędzy nawiasami kątowymi). Powyższy tekst jest maksimum tego, co twórcy kompilatorów zrobili dla złagodzenia problemu – wcześniej kompilatory wypluwały z siebie potok niezrozumiałych błędów i ostrzeżeń (zależnie od kontekstu) 🙂
Na czym polega problem ?
Problemem są tutaj dwa, następujące po sobie, prawe nawiasy kątowe (>>). A konkretniej to, co oznaczają one w języku C++ w różnych kontekstach. Wiemy, że taki operator jest używany w dwóch głównych kontekstach:
- jako operator przesunięcia bitowego w prawo
- jako operator przekierowania danych do strumienia
Mnogość zastosowań stała się problemem w przypadku pojawienia się dwóch takich znaków w kontenerach zagnieżdżonych.
Proces parsowania/interpretacji kodu przez kompilator składa się z kilku faz, które są wykonywane w określonej kolejności (analiza leksykalna, analiza składniowa, kontrola typów, itd.) i często są od siebie niezależne. W powyższym przypadku kompilator stwierdza problem, zanim się w ogóle zorientuje, że ma do czynienia z zagnieżdżoną strukturą. Niestety w takiej formie ten problem stał się częścią oficjalnego standardu języka.
Skąd to całe zamieszanie ?
Można powiedzieć, że problem był na tyle mały, że nie był wart jakiejś większej uwagi. Zresztą myślę, że właśnie dlatego zaakceptowano wcześniejszy stan rzeczy – skala pozytywnych zmian w standardzie C+98/03 była bardzo duża. Nie warto było tego porzucać z powodu jakichś małych niedociągnięć.
Ja jednak bardzo cenię sobie standard C++11 z powodu takich zmian – poradził on sobie z dużą ilością przeróżnych błędów, niedociągnięć, zaniechań i problemików. Błąd parsowania prawych nawiasów kątowych był właśnie takim potworkiem – niby mały, ale jednak również: niesamowicie irytujący, niepotrzebnie ignorowany i, co by nie mówić, jednak trochę zawstydzający 🙂
Kontynuuj poznawanie C++11: C++11 #6: Silne typy wyliczeniowe.
Przepraszam, czy w pierwszym przykładzie na pewno powinno być „#include int main()” napisane w jednej linii?
Nie, nie powinno – już poprawione 🙂
Powinienem bardziej zgłębić ten problem.