Jakie są różnice między koncepcjami a ograniczeniami szablonowymi?

96

Chcę wiedzieć, jakie są semantyczne różnice między propozycją pełnych koncepcji C ++ a ograniczeniami szablonowymi (na przykład ograniczenia, które pojawiły się w Dlang lub nowa propozycja konceptów lite dla C ++ 1y ).

Co potrafią pełnoprawne koncepcje, czego nie potrafią ograniczenia szablonowe?

Rayniery
źródło
2
Propozycja ograniczeń wchodzi w to.
chris
Przypomniałem sobie 4.8 Koncepcje projektowe , ale tak naprawdę nie ma zbyt wiele informacji na temat sposobu, w jaki koncepcje stanowią dalsze ograniczenia. Jeśli już, koncepcje Google mogą teraz ujawnić pewne łatwe do zauważenia różnice po uzyskaniu lepszego zrozumienia ograniczeń wynikających z propozycji.
Chris
Koncepcje IMHO poprawiają czytelność i zapewniają wyraźniejsze umiejętności programistyczne, o które dawno temu prosił Alexander Stepanov w „Elements of Programming”. Propozycja lite jest tylko krokiem w tym kierunku, aby złagodzić ciężar dziwnych ograniczeń typu enable_if, które są wymagane w tej chwili. Im szybciej, tym lepiej w programowaniu ogólnym.
dirvine

Odpowiedzi:

136

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:

  1. Ograniczenie - predykat dotyczący statycznie ocenianych właściwości typu. Wymagania czysto składniowe. To nie jest abstrakcja domeny.
  2. Aksjomaty - semantyczne wymagania typów, które zakłada się, że są prawdziwe. Nie sprawdzone statycznie.
  3. 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 Contmusi być Sortabletypem. 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ę Sortablew tej funkcji, otrzymasz niezły błąd, który natychmiast poinformuje Cię, że typ wydedukowany Tnie jest Sortabletypem. Jeśli zrobił to w C ++ 11, można by mieć jakiś straszny błąd wyrzucony z wewnątrz tej sortfunkcji, ż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:

  1. Czy ten typ ma przeciążony operator taki a taki?
  2. Czy tych typów można używać jako argumentów dla tego operatora?
  3. Czy ten typ ma taką a taką cechę?
  4. Czy to stałe wyrażenie jest temu równe? (dla argumentów szablonu innego niż typ)
  5. Czy ten typ ma funkcję o nazwie yada-yada, która zwraca ten typ?
  6. 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, Ta Unie 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 Greateraksjomat. Dobrze i dobrze jest powiedzieć, że dwa obiekty typu Tmożna porównać z operatorami >i <, ale co one oznaczają ? GreaterAksjomat mówi, że wtedy i tylko wtedy xjest większa wtedy y, wtedy yjest 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ń:

  1. Czy te dwa operatory mają ze sobą taką relację?
  2. Czy ten operator dla takiego a takiego typu oznacza to?
  3. Czy ta operacja na tym typie ma taką złożoność?
  4. 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 xi ytypu T: jeśli x == y, nowy obiekt tego samego typu utworzony przez konstrukcję kopiującą T{x} == yi 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ą Orderedkoncepcję:

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 Tbył Ordered, musi również spełniać wymagania Regularkoncepcji. RegularKoncepcja 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ń, Orderedwymaga, aby Tspełnić jedno ograniczenie i cztery aksjomaty:

  • Ograniczenie: Orderedtyp musi mieć rozszerzenie operator<. Jest to sprawdzane statycznie, więc musi istnieć.
  • Aksjomaty: Dla xi ytypu T:
    • x < y daje ścisłe uporządkowanie całkowite.
    • Kiedy xjest większe niż y, yjest mniejsze niż xi odwrotnie.
    • Kiedy xjest mniejsze lub równe y, yjest nie mniejsze niż xi odwrotnie.
    • Kiedy xjest większe lub równe y, ynie jest większe niż xi 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ć.

  • Orderedtypy obsługują operator<i mają ścisłe porządkowanie całkowite i inną semantykę porządkowania.

  • Copyabletypy można kopiować, można je zniszczyć, a jeśli xjest równe yi xjest kopiowane, kopia będzie również porównywana z y.

  • IteratorTypy musiało związane typy value_type, reference, difference_type, i iterator_categoryktó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_ifjej metaprogramowaniu.

Jest jednak kilka rzeczy, których propozycja ograniczeń nie obejmuje:

  1. Nie zawiera języka definicji pojęcia.

  2. 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.

  3. 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.

sftrabbit
źródło
5
Należy zauważyć, że Concept-lite nie porównuje ograniczeń z implementacją samego szablonu. Możesz więc twierdzić, że można użyć dowolnego DefaultConstructable, ale kompilator nie powstrzyma Cię przed użyciem konstruktora kopiującego przez przypadek. Bardziej w pełni funkcjonalna propozycja koncepcji.
Nicol Bolas
24
To jest pierwszy szkic ?!
Nicol Bolas,
2
@sftrabbit, bardzo dobra odpowiedź. Ale mam pytanie: w jaki sposób kompilator sprawdzi, czy typ spełnia semantyczne wymagania pojęcia?
Rayniery
1
Czy (przy dużej niepewności) „aksjomaty” zostaną sprawdzone w czasie wykonywania, czy też będą służyć tylko jako rodzaj znaczników obietnicy?
Red XIII
4
@ScarletAmaranth: Ponieważ sprowadza się do automatycznego udowodnienia twierdzenia w skończonym ograniczonym czasie. Są ku temu dwie przeszkody: 1. Udowodnienie dowolnego twierdzenia w skończonym czasie ograniczonym, które jest niezwykle trudne w teorii i niemożliwe przy obecnej technologii. 2. Nie możesz udowodnić aksjomatu, chyba że dowód jest oparty na innych aksjomatach. (W takim przypadku matematycznie staje się "nie aksjomatem") Te aksjomaty mają na celu przekazanie kompilatorowi "Oczywiście możesz odwrócić porównanie, jeśli uznasz to za przydatne ..." lub "Tak, zestaw EmptySet używany w przecięciu jest zawsze daje ten sam wynik ”.
Laurent LA RIZZA
4

Moje 2 centy:

  1. Propozycja Concept-Lite nie służy do „sprawdzania typu” implementacji szablonu . Oznacza to, że Concepts-lite zapewni (teoretycznie) zgodność interfejsu w miejscu tworzenia szablonu. Cytując z artykułu: „Concepts lite jest rozszerzeniem C ++, które pozwala na użycie predykatów w celu ograniczenia argumentów szablonów”. I to wszystko. Nie mówi, że treść szablonu zostanie sprawdzona (w izolacji) względem predykatów. To prawdopodobnie oznacza, że ​​nie ma pierwszorzędnego pojęcia archtypów, kiedy mówisz o concept-lite. archtypy, o ile dobrze pamiętam, w propozycjach ciężkich od koncepcji to takie, które oferują nie mniej i nic więcej, aby zadowolić realizację szablonu.

  2. Concept-lite używa gloryfikowanych funkcji constexpr z odrobiną sztuczki składniowej obsługiwanej przez kompilator. Brak zmian w regułach wyszukiwania.

  3. Od programistów nie wymaga się pisania map pojęć.

  4. Na koniec jeszcze raz cytując: „Propozycja ograniczeń nie odnosi się bezpośrednio do specyfikacji lub użycia semantyki; ma na celu jedynie sprawdzenie składni”. Oznaczałoby to, że aksjomaty nie wchodzą w zakres (na razie).

Sumant
źródło