C ++ 11 wprowadza zdefiniowane przez użytkownika literały , które pozwolą na wprowadzenie nowej składni dosłownym opartą na istniejących literały ( int
, hex
, string
, float
), tak, że każdy rodzaj będzie mógł mieć dosłownego prezentacji.
Przykłady:
// imaginary numbers
std::complex<long double> operator "" _i(long double d) // cooked form
{
return std::complex<long double>(0, d);
}
auto val = 3.14_i; // val = complex<long double>(0, 3.14)
// binary values
int operator "" _B(const char*); // raw form
int answer = 101010_B; // answer = 42
// std::string
std::string operator "" _s(const char* str, size_t /*length*/)
{
return std::string(str);
}
auto hi = "hello"_s + " world"; // + works, "hello"_s is a string not a pointer
// units
assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds
Na pierwszy rzut oka wygląda to bardzo fajnie, ale zastanawiam się, jak to naprawdę ma zastosowanie, kiedy próbowałem pomyśleć o posiadaniu sufiksów _AD
i _BC
tworzeniu dat, stwierdziłem, że jest to problematyczne ze względu na kolejność operatorów. 1974/01/06_AD
najpierw oceni 1974/01
(jako zwykłe int
), a dopiero później 06_AD
(nie mówiąc już o sierpniu i wrześniu, które muszą być zapisane bez 0
powodów ósemkowych). Można to obejść, stosując składnię 1974-1/6_AD
tak, aby kolejność oceny operatora działała, ale była niezgrabna.
Więc do czego sprowadza się moje pytanie, czy uważasz, że ta cecha usprawiedliwi się? Jakie inne literały chciałbyś zdefiniować, aby uczynić Twój kod C ++ bardziej czytelnym?
Zaktualizowano składnię, aby pasowała do ostatecznej wersji roboczej w czerwcu 2011
źródło
string operator "" _s(const char*s);"
nie można ich użyć do analizy"hello"_s"
. To jest literał łańcuchowy i będzie szukał operatora z dodatkowymsize_t
parametrem. Czy mam rację?uint16_t
których zachowanie jest zależne od implementacji, na podobneuwrap16
iunum16
których zachowanie byłoby niezależne od implementacji, takie, że biorącuwrap16 w=1; unum16 n=1;
pod uwagę wyrażeniaw-2
in-2
dałoby(uwrap16)65535
i(int)-1
odpowiednio [uint16_t
dałoby pierwszy wynik w systemach, w którychint
jest 16 bitów, a drugi w systemach, w którychint
jest większy]. Największym problemem, jaki widziałem, była obsługa literałów numerycznych.sizeof
zwracanie typów całkowitych zależnych od implementacji, ale sytuacja może być znacznie lepsza niż jest. Co myślisz o tej koncepcji?Odpowiedzi:
Oto przypadek, w którym istnieje przewaga używania literałów zdefiniowanych przez użytkownika zamiast wywołania konstruktora:
Zaletą jest to, że wyjątek czasu wykonywania jest konwertowany na błąd kompilacji. Nie można było dodać statycznego potwierdzenia do kontrolera bitset pobierającego łańcuch (przynajmniej nie bez argumentów szablonu ciągu).
źródło
Na pierwszy rzut oka wydaje się, że jest to prosty cukier syntaktyczny.
Ale patrząc głębiej, widzimy, że jest to coś więcej niż cukier składniowy, ponieważ rozszerza możliwości użytkownika C ++ o tworzenie typów zdefiniowanych przez użytkownika, które zachowują się dokładnie tak, jak odrębne typy wbudowane. W tym przypadku ten mały „bonus” jest bardzo interesującym dodatkiem C ++ 11 do C ++.
Czy naprawdę potrzebujemy tego w C ++?
Widzę kilka zastosowań w kodzie, który napisałem w ostatnich latach, ale to, że nie użyłem go w C ++, nie oznacza, że nie jest on interesujący dla innego programisty C ++ .
Użyliśmy w C ++ (i chyba w C) literałów zdefiniowanych przez kompilator, aby wpisać liczby całkowite jako krótkie lub długie liczby całkowite, liczby rzeczywiste jako zmiennoprzecinkowe lub podwójne (lub nawet długie podwójne), a ciągi znaków jako zwykłe lub szerokie znaki .
W C ++ mieliśmy możliwość tworzenia własnych typów (tj. Klas), potencjalnie bez narzutów (inlining itp.). Mieliśmy możliwość dodawania operatorów do ich typów, aby zachowywały się jak podobne typy wbudowane, co umożliwia programistom C ++ używanie macierzy i liczb zespolonych tak naturalnie, jak by to było, gdyby zostały dodane do samego języka. Możemy nawet dodać operatory rzutowania (co zwykle jest złym pomysłem, ale czasami jest to właściwe rozwiązanie).
Wciąż brakowało nam jednej rzeczy, aby typy użytkowników zachowywały się jak typy wbudowane: literały zdefiniowane przez użytkownika.
Więc myślę, że jest to naturalna ewolucja języka, ale aby być jak najbardziej kompletnym: „ Jeśli chcesz utworzyć typ i chcesz, aby zachowywał się jak typy wbudowane, oto narzędzia. .. ”
Wydaje mi się, że jest to bardzo podobne do decyzji .NET, aby uczynić każdy element pierwotny strukturą, w tym wartości logiczne, liczby całkowite itp., I wszystkie struktury pochodzą od Object. Już sama ta decyzja stawia .NET daleko poza zasięgiem Javy podczas pracy z prymitywami, bez względu na to, jak wiele hacków związanych z boksowaniem / rozpakowywaniem Java doda do swojej specyfikacji.
Czy naprawdę potrzebujesz tego w C ++?
To pytanie jest dla CIEBIE odpowiedzieć. Nie Bjarne Stroustrup. Nie Herb Sutter. Nie jest członkiem komitetu standardowego C ++. Dlatego masz wybór w C ++ i nie ograniczą one użytecznej notacji do samych typów wbudowanych.
Jeżeli ty potrzebujesz, to jest to dodatek mile widziane. Jeśli ty nie, dobrze ... Nie używaj go. Nic cię to nie kosztuje.
Witamy w C ++, języku, w którym funkcje są opcjonalne.
Nadęty??? Pokaż mi swoje kompleksy !!!
Istnieje różnica między nadętym a złożonym (gra słów zamierzona).
Jak pokazał Niels w Jakie nowe możliwości dodają literały zdefiniowane przez użytkownika do C ++? , możliwość zapisania liczby zespolonej jest jedną z dwóch funkcji dodanych „ostatnio” do C i C ++:
Teraz zarówno typ „podwójnie złożony” w C99, jak i „std :: złożony” w C ++ mogą być mnożone, dodawane, odejmowane itp. Przy użyciu przeciążania operatorów.
Ale w C99 po prostu dodali inny typ jako typ wbudowany i wbudowaną obsługę przeciążania operatora. Dodali też kolejną wbudowaną funkcję dosłowną.
W C ++ po prostu wykorzystali istniejące cechy języka, zobaczyli, że funkcja dosłowna była naturalną ewolucją języka, i w ten sposób ją dodali.
W C, jeśli potrzebujesz tego samego ulepszenia notacji dla innego typu, nie masz szczęścia, dopóki nie zaczniesz lobbować, aby dodać funkcje fal kwantowych (lub punkty 3D, lub jakikolwiek podstawowy typ, którego używasz w swojej dziedzinie) do Standard C jako typ wbudowany kończy się sukcesem.
W C ++ 11 możesz to zrobić sam:
Czy jest nadęty? Nie , istnieje taka potrzeba, o czym świadczy, jak kompleksy C i C ++ potrzebują sposobu reprezentowania ich dosłownych wartości zespolonych.
Czy jest źle zaprojektowany? Nie , została zaprojektowana jak każda inna funkcja C ++, z myślą o rozszerzalności.
Czy jest to tylko do celów notacji? Nie , ponieważ może nawet dodać bezpieczeństwo typów do twojego kodu.
Na przykład, wyobraźmy sobie kod zorientowany na CSS:
Wówczas bardzo łatwo jest wymusić mocne wpisywanie w przypisaniu wartości.
Czy jest niebezpieczny?
Dobre pytanie. Czy te funkcje mogą mieć przestrzeń nazw? Jeśli tak, to Jackpot!
W każdym razie, jak wszystko, możesz się zabić, jeśli narzędzie zostanie niewłaściwie użyte . C jest potężny i możesz odstrzelić sobie głowę, jeśli niewłaściwie użyjesz pistoletu C. C ++ ma pistolet C, ale także skalpel, paralizator i inne narzędzie, które znajdziesz w zestawie narzędzi. Możesz niewłaściwie użyć skalpela i wykrwawić się na śmierć. Lub możesz zbudować bardzo elegancki i solidny kod.
Tak więc, jak każda funkcja C ++, czy naprawdę jej potrzebujesz? Jest to pytanie, na które musisz odpowiedzieć, zanim użyjesz go w C ++. Jeśli tego nie zrobisz, nic Cię to nie kosztuje. Ale jeśli naprawdę tego potrzebujesz, język cię nie zawiedzie.
Przykład daty?
Wydaje mi się, że twoim błędem jest to, że mieszasz operatory:
Nie można tego uniknąć, ponieważ / będąc operatorem, kompilator musi to zinterpretować. I AFAIK, to dobra rzecz.
Aby znaleźć rozwiązanie twojego problemu, napisałbym dosłownie w inny sposób. Na przykład:
Osobiście wybrałbym liczbę całkowitą i daty ISO, ale to zależy od TWOICH potrzeb. I o to właśnie chodzi w umożliwieniu użytkownikowi definiowania własnych nazw literałów.
źródło
you can write 1+2i, but you still can't write a+bi, so there's absolutely no point
Nawet ignorowanie twojegoa+bi
przykładu jest śmieszne, fakt, że postrzegasz go jako „niską częstotliwość”, nie oznacza, że wszyscy tak robią. . . Patrząc z szerszej perspektywy, chodzi o to, aby upewnić się, że obiekty zdefiniowane przez użytkownika mogą być w jak największym stopniu uważane za pierwszorzędnych obywateli języka, podobnie jak typy wbudowane. Więc jeśli potrafisz pisać1.5f
i1000UL
dlaczego nie możesz pisać,25i
a nawet100101b
? W przeciwieństwie do C i Javy, typy użytkowników nie są uważane za obywateli drugiej kategorii języka w C ++.Most of data still comes from IO
W kodzie jest wiele wartości zakodowanych na stałe. Spójrz na wszystkie logiczne, wszystkie liczby całkowite, wszystkie podwojenia występujące w kodzie, ponieważ wygodniej jest pisaćx = 2 * y ;
zamiastx = Two * y
gdzieTwo
jest to stała o silnym typie . Literały zdefiniowane przez użytkownika pozwalają nam umieścić na nim typ i napisać:x = 2_speed * y ;
i poprosić kompilator o sprawdzenie, czy obliczenia mają sens. . . Wszystko sprowadza się do silnego pisania na klawiaturze. . . Być może nie będziesz go używać. Ale na pewno tak zrobię, gdy tylko będę mógł używać w pracy kompilatora obsługującego C ++ 11.Jest to bardzo miłe dla kodu matematycznego. Z głowy widzę zastosowanie następujących operatorów:
deg dla stopni. To sprawia, że pisanie kątów absolutnych jest znacznie bardziej intuicyjne.
Może być również używany do różnych reprezentacji punktów stałych (które są nadal używane w dziedzinie DSP i grafiki).
Wyglądają na ładne przykłady, jak go używać. Pomagają uczynić stałe w kodzie bardziej czytelnymi. To kolejne narzędzie, które sprawia, że kod staje się nieczytelny, ale mamy już tyle narzędzi, że jedno więcej nie boli.
źródło
UDL to przestrzenie nazw (i można je importować przy użyciu deklaracji / dyrektyw, ale nie można jawnie określać przestrzeni nazw literałów takich jak
3.14std::i
), co oznacza, że (miejmy nadzieję) nie będzie wielu kolizji.Fakt, że faktycznie można je tworzyć za pomocą szablonów (i konstruować), oznacza, że można zrobić całkiem potężne rzeczy z UDL. Autorzy Biginta będą naprawdę szczęśliwi, ponieważ mogą wreszcie mieć dowolnie duże stałe, obliczane w czasie kompilacji (za pomocą constexpr lub szablonów).
Jest mi tylko przykro, że nie zobaczymy w standardzie kilku przydatnych literałów (z wyglądu), takich jak
s
forstd::string
ii
dla wyimaginowanej jednostki.Ilość czasu kodowania, który zostanie zaoszczędzony przez UDL, w rzeczywistości nie jest tak duża, ale czytelność zostanie znacznie zwiększona, a coraz więcej obliczeń można przesunąć do czasu kompilacji w celu szybszego wykonania.
źródło
Pozwólcie, że dodam trochę kontekstu. W naszej pracy bardzo potrzebne są literały zdefiniowane przez użytkownika. Pracujemy na MDE (Model-Driven Engineering). Chcemy definiować modele i metamodele w C ++. W rzeczywistości zaimplementowaliśmy mapowanie z Ecore do C ++ ( EMF4CPP ).
Problem pojawia się, gdy można zdefiniować elementy modelu jako klasy w C ++. Podejmujemy podejście polegające na przekształceniu metamodelu (Ecore) w szablony z argumentami. Argumentami szablonu są cechy strukturalne typów i klas. Na przykład klasa z dwoma atrybutami int wyglądałaby tak:
Jednak okazuje się, że każdy element w modelu lub metamodelu ma zwykle nazwę. Chcielibyśmy napisać:
ALE, C ++ ani C ++ 0x nie pozwalają na to, ponieważ ciągi znaków są zabronione jako argumenty szablonów. Możesz napisać imię char, ale jest to wprawdzie bałagan. Mając odpowiednie literały zdefiniowane przez użytkownika, moglibyśmy napisać coś podobnego. Powiedzmy, że używamy „_n” do identyfikacji nazw elementów modelu (nie używam dokładnej składni, tylko po to, żeby zrobić pomysł):
Wreszcie, posiadanie tych definicji jako szablonów bardzo pomaga nam w projektowaniu algorytmów przechodzenia przez elementy modelu, transformacje modelu itp., Które są naprawdę wydajne, ponieważ informacje o typie, identyfikacja, transformacje itp. Są określane przez kompilator w czasie kompilacji.
źródło
by the compiler at compile time
część ... :-)Bjarne Stroustrup mówi o UDL w tym wykładzie C ++ 11 , w pierwszej sekcji o interfejsach bogatych w typy, około 20 minut.
Jego podstawowy argument na rzecz UDL ma formę sylogizmu:
Typy „trywialne”, tj. Wbudowane typy pierwotne, mogą wychwytywać tylko trywialne błędy. Interfejsy z bogatszymi typami umożliwiają systemowi typów wychwytywanie większej liczby rodzajów błędów.
Rodzaje błędów typu, które mogą zostać wyłapane przez bogato wpisany kod, mają wpływ na rzeczywisty kod. (Podaje przykład Mars Climate Orbiter, który niesławnie zawiódł z powodu błędu wymiarów w ważnej stałej).
W prawdziwym kodzie jednostki są rzadko używane. Ludzie ich nie używają, ponieważ tworzenie typów bogatych w czasie wykonywania lub narzut pamięci jest zbyt kosztowne, a używanie wcześniej istniejącego kodu jednostkowego opartego na szablonie C ++ jest tak brzydkie, że nikt go nie używa. (Z empirycznego punktu widzenia nikt go nie używa, mimo że biblioteki istnieją już od dekady).
Dlatego, aby skłonić inżynierów do używania jednostek w rzeczywistym kodzie, potrzebowaliśmy urządzenia, które (1) nie powoduje narzutu czasu wykonania i (2) jest notacyjnie akceptowalne.
źródło
Obsługa sprawdzania wymiarów w czasie kompilacji jest jedynym wymaganym uzasadnieniem.
Zobacz na przykład PhysUnits-CT-Cpp11 , małą bibliotekę C ++ 11, C ++ 14 tylko z nagłówkiem do analizy wymiarowej w czasie kompilacji oraz manipulacji i konwersji jednostek / ilości. Prostsze niż Boost.Units obsługuje literały symboli jednostek, takie jak m, g, s, prefiksy metryczne, takie jak m, k, M, zależy tylko od standardowej biblioteki C ++, tylko SI, całkowych potęg wymiarów.
źródło
Hmm ... jeszcze nie myślałem o tej funkcji. Twoja próbka została dobrze przemyślana i na pewno jest interesująca. C ++ jest bardzo potężny, tak jak jest teraz, ale niestety składnia używana w fragmentach kodu, które czytasz, jest czasami zbyt złożona. Czytelność to jeśli nie wszystko, to przynajmniej dużo. Taka funkcja byłaby nastawiona na większą czytelność. Jeśli wezmę twój ostatni przykład
... Ciekawe, jak byś to dziś wyraził. Miałbyś klasę KG i LB i porównywałbyś niejawne obiekty:
I to też by się nadało. W przypadku typów, które mają dłuższe nazwy lub typy, których nie masz nadziei na posiadanie tak fajnego konstruktora bez pisania adaptera, może to być fajny dodatek do niejawnego tworzenia i inicjowania obiektów w locie. Z drugiej strony możesz już tworzyć i inicjować obiekty za pomocą metod.
Ale zgadzam się z Nilsem w matematyce. Na przykład funkcje trygonometryczne C i C ++ wymagają danych wejściowych w radianach. Myślę jednak w stopniach, więc bardzo krótka niejawna konwersja, taka jak opublikowana przez Nilsa, jest bardzo przyjemna.
Ostatecznie będzie to cukier syntaktyczny, ale będzie miał niewielki wpływ na czytelność. I prawdopodobnie łatwiej będzie też napisać niektóre wyrażenia (sin (180,0 stopni) jest łatwiejsze do napisania niż sin (stopnie (180,0)). I wtedy będą ludzie, którzy nadużywają tego pojęcia. Ale wtedy osoby nadużywające języka powinny używać bardziej restrykcyjne języki niż coś tak wyrazistego jak C ++.
Ach, mój post nie mówi w zasadzie nic poza: wszystko będzie dobrze, wpływ nie będzie zbyt duży. Nie martwmy się. :-)
źródło
Nigdy nie potrzebowałem ani nie chciałem tej funkcji (ale może to być efekt Blub ). Moją odruchową reakcją jest to, że jest to kiepskie i prawdopodobnie spodoba się tym samym ludziom, którzy uważają, że fajnie jest przeciążać operatora + dla dowolnej operacji, którą można zdalnie zinterpretować jako dodawanie.
źródło
C ++ jest zwykle bardzo rygorystyczny co do używanej składni - poza preprocesorem niewiele można użyć do zdefiniowania własnej składni / gramatyki. Np. Możemy przeciążać istniejące operaty, ale nie możemy zdefiniować nowych - IMO jest to bardzo zgodne z duchem C ++.
Nie mam nic przeciwko niektórym sposobom na bardziej spersonalizowany kod źródłowy - ale wybrany punkt wydaje mi się bardzo odizolowany, co najbardziej mnie dezorientuje.
Nawet zamierzone użycie może znacznie utrudnić odczytanie kodu źródłowego: pojedyncza litera może mieć daleko idące skutki uboczne, których w żaden sposób nie można zidentyfikować na podstawie kontekstu. W przypadku symetrii u, l if większość programistów wybierze pojedyncze litery.
Może to również zmienić zakres w problem, używanie pojedynczych liter w globalnej przestrzeni nazw prawdopodobnie zostanie uznane za złą praktykę, a narzędzia, które mają być łatwiejsze do mieszania bibliotek (przestrzenie nazw i identyfikatory opisowe), prawdopodobnie zniweczą jego cel.
Widzę pewne zalety w połączeniu z "auto", także w połączeniu z biblioteką jednostek , taką jak jednostki doładowania , ale nie na tyle, aby zasłużyć na to dodanie.
Zastanawiam się jednak, jakie sprytne pomysły wpadamy na pomysł.
źródło
using single letters in global namespace will probably be considered bad practice
Ale to nie ma znaczenia: (A) UDL muszą być zdefiniowane w (nieglobalnym) zakresie przestrzeni nazw ... prawdopodobnie dlatego, że (B) muszą składać się z podkreślenia, a następnie> = 1 litera, a nie tylko litery i takich identyfikatorów w globalne NS są zarezerwowane do realizacji. To co najmniej 2 punkty przeciwko pomysłowi, że UDL z natury wywołuje zamieszanie. Jeśli chodzi o konieczność ograniczania przestrzeni nazw, zmniejszając użyteczność funkcji, dlatego np. Standardowa biblioteka deklaruje je winline namespace
s, aby użytkownicy mogli importować hurtowo w razie potrzeby.Użyłem literałów użytkownika dla ciągów binarnych, takich jak:
używając
std::string(str, n)
konstruktora, aby\0
aby nie przeciąć łańcucha na pół. (Projekt wykonuje dużo pracy z różnymi formatami plików.)Było to pomocne również wtedy, gdy porzuciłem
std::string
na rzecz opakowaniastd::vector
.źródło
Szum linii w tym urządzeniu jest ogromny. Poza tym strasznie się to czyta.
Daj mi znać, czy uzasadnili ten nowy dodatek składni jakimikolwiek przykładami? Na przykład, czy mają kilka programów, które już używają C ++ 0x?
Dla mnie ta część:
Nie uzasadnia tej części:
Nawet jeśli użyjesz składni i w 1000 innych liniach. Jeśli piszesz, prawdopodobnie napiszesz również 10000 wierszy czegoś innego. Zwłaszcza, gdy nadal będziesz prawdopodobnie pisać głównie wszędzie to:
Słowo kluczowe „auto” może być uzasadnione, tylko być może. Ale weźmy tylko C ++, ponieważ pod tym względem jest lepszy niż C ++ 0x.
To jest ... takie proste. Nawet jeśli wszystkie standardowe i spiczaste nawiasy są po prostu kiepskie, jeśli używasz ich wszędzie. Nie zaczynam zgadywać, jaka jest składnia w C ++ 0x, aby zmienić std :: złożone w złożone.
To może coś prostego, ale nie sądzę, aby było to takie proste w C ++ 0x.
Być może? > :)
W każdym razie chodzi o: napisanie 3.14i zamiast std :: complex (0, 3.14); ogólnie nie oszczędza dużo czasu, z wyjątkiem kilku super specjalnych przypadków.
źródło
std::complex<double> val(0, 3.14);
.