Jakie nowe możliwości dodają literały zdefiniowane przez użytkownika do C ++?

139

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 _ADi _BCtworzeniu dat, stwierdziłem, że jest to problematyczne ze względu na kolejność operatorów. 1974/01/06_ADnajpierw 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 0powodów ósemkowych). Można to obejść, stosując składnię 1974-1/6_ADtak, 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

Motti
źródło
8
Zagłosuję za zamknięciem tego. Tytuł jest dość wyraźnie zapalny.
Puppy
76
@DeadMG, jeśli masz problem z tytułem to możesz go edytować. Trochę zabawne jest zamknięcie pytania sprzed 3 lat, które ma 11 głosów za i 8 ulubionych. (Nie wspominając, że etykieta na tej stronie zmieniła się w ciągu ostatnich 3 lat).
Motti
5
Myślę, że masz błąd w przykładach: 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 dodatkowym size_tparametrem. Czy mam rację?
towi
1
Zastanawiałem się nad tym, czy sensowne byłoby pisanie „przenośnego C” w C ++, zastępując typy, takie jak uint16_tktórych zachowanie jest zależne od implementacji, na podobne uwrap16i unum16których zachowanie byłoby niezależne od implementacji, takie, że biorąc uwrap16 w=1; unum16 n=1;pod uwagę wyrażenia w-2i n-2dałoby (uwrap16)65535i (int)-1odpowiednio [ uint16_tdałoby pierwszy wynik w systemach, w których intjest 16 bitów, a drugi w systemach, w których intjest większy]. Największym problemem, jaki widziałem, była obsługa literałów numerycznych.
supercat
1
Możliwość sprawnego współdziałania literałów numerycznych z innymi typami liczbowymi o zdefiniowanym zachowaniu powinna pozwolić na użycie takich typów do stworzenia języka, w którym kod, który chciałby wykonywać działania zależne od implementacji, mógłby to zrobić bez konieczności polegania na implementacji. zdefiniowane zachowania. Jest kilka miejsc, w których IDB nadal będzie nieuniknione, ponieważ takie rzeczy jak różnice wskaźników i sizeofzwracanie typów całkowitych zależnych od implementacji, ale sytuacja może być znacznie lepsza niż jest. Co myślisz o tej koncepcji?
supercat

Odpowiedzi:

71

Oto przypadek, w którym istnieje przewaga używania literałów zdefiniowanych przez użytkownika zamiast wywołania konstruktora:

#include <bitset>
#include <iostream>

template<char... Bits>
  struct checkbits
  {
    static const bool valid = false;
  };

template<char High, char... Bits>
  struct checkbits<High, Bits...>
  {
    static const bool valid = (High == '0' || High == '1')
                   && checkbits<Bits...>::valid;
  };

template<char High>
  struct checkbits<High>
  {
    static const bool valid = (High == '0' || High == '1');
  };

template<char... Bits>
  inline constexpr std::bitset<sizeof...(Bits)>
  operator"" _bits() noexcept
  {
    static_assert(checkbits<Bits...>::valid, "invalid digit in binary string");
    return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
  }

int
main()
{
  auto bits = 0101010101010101010101010101010101010101010101010101010101010101_bits;
  std::cout << bits << std::endl;
  std::cout << "size = " << bits.size() << std::endl;
  std::cout << "count = " << bits.count() << std::endl;
  std::cout << "value = " << bits.to_ullong() << std::endl;

  //  This triggers the static_assert at compile time.
  auto badbits = 2101010101010101010101010101010101010101010101010101010101010101_bits;

  //  This throws at run time.
  std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101_bits");
}

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

emsr
źródło
7
Możesz zrobić to samo, dając std :: bitset odpowiedniego konstruktora constexpr.
Nicol Bolas
1
@NicolBolas Masz rację. Właściwie jestem zaskoczony, że jednego tam nie ma. Może powinniśmy zaproponować jeden lub dwa na 2014 rok, jeśli nie jest za późno.
emsr
192

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 ++:

// C89:
MyComplex z1 = { 1, 2 } ;

// C99: You'll note I is a macro, which can lead
// to very interesting situations...
double complex z1 = 1 + 2*I;

// C++:
std::complex<double> z1(1, 2) ;

// C++11: You'll note that "i" won't ever bother
// you elsewhere
std::complex<double> z1 = 1 + 2_i ;

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:

Point p = 25_x + 13_y + 3_z ; // 3D point

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:

css::Font::Size p0 = 12_pt ;       // Ok
css::Font::Size p1 = 50_percent ;  // Ok
css::Font::Size p2 = 15_px ;       // Ok
css::Font::Size p3 = 10_em ;       // Ok
css::Font::Size p4 = 15 ;         // ERROR : Won't compile !

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:

1974/01/06AD
    ^  ^  ^

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:

"1974-01-06"_AD ;   // ISO-like notation
"06/01/1974"_AD ;   // french-date-like notation
"jan 06 1974"_AD ;  // US-date-like notation
19740106_AD ;       // integer-date-like notation

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.

paercebal
źródło
1
Dziękuję, ja i moje inne alternatywne osobowości zostały odkryte. Mówiąc poważnie, napisałem to sam, ale być może używam wyrażeń w moim ojczystym języku i nie tłumaczą one dobrze na angielski.
paercebal
Jeśli chodzi o "różne części", o czym świadczą ich tytuły, przepraszam, to chyba organizuje dość długi post. Jeśli chodzi o pogrubiony tekst, to jest to podsumowanie akapitu, w którym się znajdują. Osoby, które chcą tylko informacji bez uzasadnienia, mogą ograniczyć czytanie tytułów i pogrubionego tekstu.
paercebal
3
+1. Naprawdę miłe wyjaśnienie. Czekamy, aż zostanie to wdrożone. To jest dla nas naprawdę ważne. Pracujemy nad MDE (Model-Driven Engineering) i uważamy to za konieczność. Poniżej dodaję odpowiedź, aby wyjaśnić naszą sprawę.
Diego Sevilla
9
@TGV:: you can write 1+2i, but you still can't write a+bi, so there's absolutely no pointNawet ignorowanie twojego a+biprzykł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.5fi 1000ULdlaczego nie możesz pisać, 25ia nawet 100101b? W przeciwieństwie do C i Javy, typy użytkowników nie są uważane za obywateli drugiej kategorii języka w C ++.
paercebal
3
@Anton:: Most of data still comes from IOW 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 ;zamiast x = Two * ygdzie Twojest 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.
paercebal
36

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.

double operator ""_deg(long double d)
{ 
    // returns radians
    return d*M_PI/180; 
}

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

int operator ""_fix(long double d)
{ 
    // returns d as a 1.15.16 fixed point number
    return (int)(d*65536.0f); 
}

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.

Nils Pipenbrinck
źródło
1
„ale mamy już tyle nadużyć narzędzi, że jedno więcej nie boli zbytnio. ” Wow, mam nadzieję, że nie jest to filozofia stojąca za tym całym zalewaniem funkcji c ++ [x] 1234567890. Wyobraź sobie, że musisz nauczyć się biblioteki, która używa tych wszystkich, a także dziesięciu formatów plików do konfiguracji i dwóch narzędzi do wstępnego i
końcowego
@masterxilo Oczywiście, w rzeczywistości twój przykład jest absurdalny: UDL nie jest trudniejszy do nauczenia niż funkcje, tak jak dla nich tylko cukier składniowy - a poza tym każda dobra biblioteka używa tylko funkcji wymaganych do poprawy UX - i dokumentuje dokładnie to, co jest wszystkie środki. Jeśli ktoś nadużywa funkcji do generowania nieczytelnego kodu (zakładając, że w ogóle można tego uniknąć w jego pracy ...), nie powoduje to winy tej funkcji, tylko użycie. Poza tym to, co nieczytelne dla jednej osoby, jest chlebem powszednim dla drugiej. To wszystko opinie - i opcje . Jeśli ich nie lubisz, nie martw się! Nie musisz ich używać. Inni mogą .
underscore_d
17

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 sfor std::stringii 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.

coppro
źródło
Dzięki za wyjaśnienie kwestii przestrzeni nazw ... Zastanawiałem się nad tym.
Nathan Reed
12

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:

typedef ::ecore::Class< Attribute<int>, Attribute<int> > MyClass;

Jednak okazuje się, że każdy element w modelu lub metamodelu ma zwykle nazwę. Chcielibyśmy napisać:

typedef ::ecore::Class< "MyClass", Attribute< "x", int>, Attribute<"y", int> > MyClass;

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ł):

typedef ::ecore::Class< MyClass_n, Attribute< x_n, int>, Attribute<y_n, int> > MyClass;

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.

Diego Sevilla
źródło
4
Lubię bardzo dużo się by the compiler at compile timeczęść ... :-)
paercebal
12

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:

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

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

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

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

masonk
źródło
9

Obsługa sprawdzania wymiarów w czasie kompilacji jest jedynym wymaganym uzasadnieniem.

auto force = 2_N; 
auto dx = 2_m; 
auto energy = force * dx; 

assert(energy == 4_J); 

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.

Martin Moene
źródło
Lub zobacz jednostki , bibliotekę do analizy wymiarowej i konwersji jednostek utworzoną w czasie kompilacji, zawierającą tylko nagłówki, opartą na języku C ++ 14 bez żadnych zależności autorstwa Nic Holthaus .
Martin Moene,
6

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

assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

... Ciekawe, jak byś to dziś wyraził. Miałbyś klasę KG i LB i porównywałbyś niejawne obiekty:

assert(KG(1.0f) == LB(2.2f));

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ę. :-)

mstrobl
źródło
5
Twoje nawiasy są niezrównoważone! Przepraszam, mój OCD też mnie nienawidzi.
X-Istence
3

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.

fizzer
źródło
Potwierdzam: bardzo ciekawy artykuł.
paercebal
2

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

peterchen
źródło
1
using single letters in global namespace will probably be considered bad practiceAle 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 w inline namespaces, aby użytkownicy mogli importować hurtowo w razie potrzeby.
underscore_d
2

Użyłem literałów użytkownika dla ciągów binarnych, takich jak:

 "asd\0\0\0\1"_b

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::stringna rzecz opakowania std::vector.

rr-
źródło
-5

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ęść:

auto val = 3.14_i

Nie uzasadnia tej części:

std::complex<double> operator ""_i(long double d) // cooked form
{ 
    return std::complex(0, d);
}

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:

std::complex<double> val = 3.14i

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.

std::complex<double> val = std::complex(0, 3.14);

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.

complex = std::complex<double>;

To może coś prostego, ale nie sądzę, aby było to takie proste w C ++ 0x.

typedef std::complex<double> complex;

complex val = std::complex(0, 3.14);

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.

Wesoły
źródło
10
@Cheery: Dla ciebie "auto val = 3.14i" nie usprawiedliwia kodu napisanego w celu jego obsługi. Mógłbym odpowiedzieć, że dla mnie "printf ("% i ", 25)" nie uzasadnia kodu napisanego dla printf. Czy widzisz wzór?
paercebal
5
@Cheery: "Szum linii w tym czymś jest ogromny". Nie, to nie jest… „Poza tym strasznie się to czyta”. Twój subiektywny argument jest interesujący, ale powinieneś przyjrzeć się ogólnie przeciążeniu operatorów, aby zobaczyć, że kod tej funkcji jest daleki od zaskoczenia / szokowania ... Dla programisty C ++
paercebal
3
auto pomoże czytelności. rozważ użycie interatorów w pętli for: for (auto it = vec.begin (); it! = vec.end (); ++ it) ... Wiem o for_each, ale nie lubię tworzyć funktora, aby go używać .
KitsuneYMG
1
@kts: W C ++ 1x będziemy mieli lambdę i zakres dla
Joe D
3
Jeśli twoja linia C ++ jest lepsza niż C ++ 0x, to moja linia jest jeszcze lepsza. Napisz tylko: std::complex<double> val(0, 3.14);.
Ben Voigt,