Poniższe informacje są nieaktualne. Musi zostać zaktualizowany zgodnie z najnowszą wersją roboczą Concepts Lite.
Sekcja 3 propozycji ograniczeń obejmuje to w rozsądny sposób.
Propozycja koncepcji została odłożona na chwilę na dalszy plan w nadziei, że ograniczenia (tj. Concept-lite) można rozwinąć i zaimplementować w krótszej skali czasowej, obecnie dążąc przynajmniej do czegoś w C ++ 14. Propozycja ograniczeń ma działać jako płynne przejście do późniejszej definicji pojęć. Ograniczenia są częścią propozycji koncepcji i są niezbędnym elementem składowym jej definicji.
Podczas projektowania bibliotek koncepcyjnych dla C ++ , Sutton i Stroustrup rozważają następującą zależność:
Pojęcia = ograniczenia + aksjomaty
Aby szybko podsumować ich znaczenie:
- Ograniczenie - predykat dotyczący statycznie ocenianych właściwości typu. Wymagania czysto składniowe. To nie jest abstrakcja domeny.
- Aksjomaty - semantyczne wymagania typów, które zakłada się, że są prawdziwe. Nie sprawdzone statycznie.
- Pojęcia - Ogólne, abstrakcyjne wymagania algorytmów dotyczące ich argumentów. Zdefiniowane w kategoriach ograniczeń i aksjomatów.
Więc jeśli dodasz aksjomaty (właściwości semantyczne) do ograniczeń (właściwości składniowe), otrzymasz pojęcia.
Concepts-Lite
Propozycja Concept-Lite niesie nam tylko pierwszą część, ograniczenia, ale jest to ważny i konieczny krok w kierunku pełnoprawnych koncepcji.
Ograniczenia
W ograniczeniach chodzi o składnię . Dają nam sposób statycznego rozróżniania właściwości typu w czasie kompilacji, dzięki czemu możemy ograniczyć typy używane jako argumenty szablonu na podstawie ich właściwości składniowych. W obecnej propozycji ograniczeń są one wyrażane za pomocą podzbioru rachunku zdań przy użyciu łączników logicznych, takich jak &&
i ||
.
Przyjrzyjmy się ograniczeniu w akcji:
template <typename Cont>
requires Sortable<Cont>()
void sort(Cont& container);
Tutaj definiujemy szablon funkcji o nazwie sort
. Nowym dodatkiem jest klauzula wymagań . Klauzula require daje pewne ograniczenia dotyczące argumentów szablonu dla tej funkcji. W szczególności to ograniczenie mówi, że typ Cont
musi być Sortable
typem. Fajną rzeczą jest to, że można to zapisać w bardziej zwięzłej formie jako:
template <Sortable Cont>
void sort(Cont& container);
Teraz, jeśli spróbujesz przekazać cokolwiek, co nie jest brane pod uwagę Sortable
w tej funkcji, otrzymasz niezły błąd, który natychmiast poinformuje Cię, że typ wydedukowany T
nie jest Sortable
typem. Jeśli zrobił to w C ++ 11, można by mieć jakiś straszny błąd wyrzucony z wewnątrz tej sort
funkcji, że nie ma sensu nikogo.
Predykaty ograniczeń są bardzo podobne do cech typu. Biorą jakiś szablon typu argumentu i podają kilka informacji na jego temat. Ograniczenia próbują odpowiedzieć na następujące rodzaje pytań dotyczących typu:
- Czy ten typ ma przeciążony operator taki a taki?
- Czy tych typów można używać jako argumentów dla tego operatora?
- Czy ten typ ma taką a taką cechę?
- Czy to stałe wyrażenie jest temu równe? (dla argumentów szablonu innego niż typ)
- Czy ten typ ma funkcję o nazwie yada-yada, która zwraca ten typ?
- Czy ten typ spełnia wszystkie wymagania składniowe, aby go używać?
Jednak ograniczenia nie mają na celu zastąpienia cech typu. Zamiast tego będą pracować ramię w ramię. Niektóre cechy typu można teraz definiować w kategoriach pojęć, a inne w kategoriach cech typu.
Przykłady
Tak więc ważną rzeczą dotyczącą ograniczeń jest to, że nie dbają o semantykę ani na jotę. Oto kilka dobrych przykładów ograniczeń:
Equality_comparable<T>
: Sprawdza, czy typ ma ==
oba operandy tego samego typu.
Equality_comparable<T,U>
: Sprawdza, czy istnieje ==
lewy i prawy operand podanych typów
Arithmetic<T>
: Sprawdza, czy typ jest typem arytmetycznym.
Floating_point<T>
: Sprawdza, czy typ jest typem zmiennoprzecinkowym.
Input_iterator<T>
: Sprawdza, czy typ obsługuje operacje składniowe, które musi obsługiwać iterator wejściowy.
Same<T,U>
: Sprawdza, czy dany typ jest taki sam.
Możesz wypróbować to wszystko dzięki specjalnej, koncepcyjnej wersji GCC .
Beyond Concepts-Lite
Teraz zajmiemy się wszystkim poza koncepcją lite propozycji. To jest nawet bardziej futurystyczne niż sama przyszłość. Od tego momentu wszystko prawdopodobnie trochę się zmieni.
Aksjomaty
Aksjomaty dotyczą semantyki . Określają relacje, niezmienniki, gwarancje złożoności i inne tego typu rzeczy. Spójrzmy na przykład.
Chociaż Equality_comparable<T,U>
ograniczenie powie ci, że istnieje obiekt, operator==
który przyjmuje typy, T
a U
nie mówi ci, co oznacza ta operacja . W tym celu będziemy mieli aksjomat Equivalence_relation
. Ten aksjomat mówi, że gdy porównuje się przedmioty tych dwóch typów z operator==
dawaniem true
, to przedmioty te są równoważne. Może się to wydawać zbędne, ale z pewnością tak nie jest. Możesz łatwo zdefiniować, operator==
że zamiast tego zachowuje się jak operator<
. Byłbyś zły, gdybyś to zrobił, ale mógłbyś.
Innym przykładem jest Greater
aksjomat. Dobrze i dobrze jest powiedzieć, że dwa obiekty typu T
można porównać z operatorami >
i <
, ale co one oznaczają ? Greater
Aksjomat mówi, że wtedy i tylko wtedy x
jest większa wtedy y
, wtedy y
jest mniej niż x
. Proponowana specyfikacja taka aksjomat wygląda następująco:
template<typename T>
axiom Greater(T x, T y) {
(x>y) == (y<x);
}
Tak więc aksjomaty odpowiadają na następujące rodzaje pytań:
- Czy te dwa operatory mają ze sobą taką relację?
- Czy ten operator dla takiego a takiego typu oznacza to?
- Czy ta operacja na tym typie ma taką złożoność?
- Czy ten wynik tego operatora sugeruje, że to prawda?
Oznacza to, że zajmują się wyłącznie semantyką typów i operacjami na tych typach. Tych rzeczy nie można sprawdzić statycznie. Jeśli trzeba to sprawdzić, typ musi w jakiś sposób oświadczyć, że jest zgodny z tą semantyką.
Przykłady
Oto kilka typowych przykładów aksjomatów:
Equivalence_relation
: Jeśli porównują się dwa obiekty ==
, są one równoważne.
Greater
: Kiedykolwiek x > y
, następnie y < x
.
Less_equal
: Kiedykolwiek x <= y
, następnie !(y < x)
.
Copy_equality
: Dla x
i y
typu T
: jeśli x == y
, nowy obiekt tego samego typu utworzony przez konstrukcję kopiującą T{x} == y
i nadal x == y
(to znaczy jest nieniszczący).
Koncepcje
Teraz pojęcia są bardzo łatwe do zdefiniowania; są po prostu połączeniem ograniczeń i aksjomatów . Zapewniają abstrakcyjne wymaganie dotyczące składni i semantyki typu.
Jako przykład rozważ następującą Ordered
koncepcję:
concept Ordered<Regular T> {
requires constraint Less<T>;
requires axiom Strict_total_order<less<T>, T>;
requires axiom Greater<T>;
requires axiom Less_equal<T>;
requires axiom Greater_equal<T>;
}
Najpierw należy zauważyć, że aby typ szablonu T
był Ordered
, musi również spełniać wymagania Regular
koncepcji. Regular
Koncepcja jest bardzo podstawowe wymagania, że typ jest dobrze ułożona - może być wykonana, zniszczony, kopiowany i porównywane.
W uzupełnieniu do tych wymagań, Ordered
wymaga, aby T
spełnić jedno ograniczenie i cztery aksjomaty:
- Ograniczenie:
Ordered
typ musi mieć rozszerzenie operator<
. Jest to sprawdzane statycznie, więc musi istnieć.
- Aksjomaty: Dla
x
i y
typu T
:
x < y
daje ścisłe uporządkowanie całkowite.
- Kiedy
x
jest większe niż y
, y
jest mniejsze niż x
i odwrotnie.
- Kiedy
x
jest mniejsze lub równe y
, y
jest nie mniejsze niż x
i odwrotnie.
- Kiedy
x
jest większe lub równe y
, y
nie jest większe niż x
i na odwrót.
Połączenie takich ograniczeń i aksjomatów daje ci koncepcje. Definiują wymagania składniowe i semantyczne dla typów abstrakcyjnych używanych z algorytmami. Obecnie algorytmy muszą zakładać, że użyte typy będą obsługiwać określone operacje i wyrażać określoną semantykę. Dzięki koncepcjom będziemy w stanie zapewnić spełnienie wymagań.
W najnowszym projekcie koncepcyjnym kompilator sprawdza tylko, czy wymagania składniowe pojęcia są spełnione przez argument szablonu. Aksjomaty pozostają niezaznaczone. Ponieważ aksjomaty oznaczają semantykę, która nie jest statycznie oceniana (lub często niemożliwa do całkowitego sprawdzenia), autor typu musiałby wyraźnie stwierdzić, że ich typ spełnia wszystkie wymagania pojęcia. Było to znane jako mapowanie koncepcyjne w poprzednich projektach, ale od tego czasu zostało usunięte.
Przykłady
Oto kilka przykładów pojęć:
Regular
typy można skonstruować, zniszczyć, skopiować i można je porównać.
Ordered
typy obsługują operator<
i mają ścisłe porządkowanie całkowite i inną semantykę porządkowania.
Copyable
typy można kopiować, można je zniszczyć, a jeśli x
jest równe y
i x
jest kopiowane, kopia będzie również porównywana z y
.
Iterator
Typy musiało związane typy value_type
, reference
, difference_type
, i iterator_category
które same muszą spełniać pewne koncepcje. Muszą również wspierać operator++
i być dereferencyjnymi.
Droga do koncepcji
Ograniczenia są pierwszym krokiem w kierunku pełnej funkcji koncepcyjnej języka C ++. Są bardzo ważnym krokiem, ponieważ zapewniają statycznie egzekwowalne wymagania typów, dzięki czemu możemy pisać znacznie czystsze funkcje i klasy szablonów. Teraz możemy uniknąć niektórych trudności i brzydoty towarzyszących std::enable_if
jej metaprogramowaniu.
Jest jednak kilka rzeczy, których propozycja ograniczeń nie obejmuje:
Nie zawiera języka definicji pojęcia.
Ograniczenia nie są mapami pojęć. Użytkownik nie musi specjalnie opisywać swoich typów jako spełniających określone ograniczenia. Są one statycznie sprawdzane przy użyciu prostych funkcji języka kompilacji.
Implementacje szablonów nie są ograniczone przez ograniczenia dotyczące ich argumentów szablonów. Oznacza to, że jeśli szablon funkcji robi coś z obiektem typu ograniczonego, czego nie powinien, kompilator nie ma możliwości zdiagnozowania tego. W pełni funkcjonalna propozycja koncepcji byłaby w stanie to zrobić.
Propozycja ograniczeń została specjalnie zaprojektowana, tak aby można było wprowadzić pełną propozycję koncepcji. Przy odrobinie szczęścia ta zmiana powinna przebiegać dość płynnie. Grupa koncepcyjna chce wprowadzić ograniczenia dla C ++ 14 (lub wkrótce potem w raporcie technicznym), podczas gdy pełne koncepcje mogą zacząć pojawiać się gdzieś w okolicach C ++ 17.