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

C++11 wprowadza literały zdefiniowane przez Użytkownika, które pozwolą na wprowadzenie nowej składni dosłownej opartej na istniejących literałach (int, hex, string, float) tak, że każdy typ będzie mógł mieć dosłowną prezentację.

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 myśleć o tworzeniu przyrostków _AD i _BC stwierdziłem, że jest to problematyczne ze względu na rozkaz operatora. 1974/01/06_AD najpierw oceniłby 1974/01 (jako zwykłe int s), a dopiero później 06_AD (by nie powiedzieć, że Sierpień i Wrzesień muszą być napisane bez 0 z powodów ósemkowych). Można to obejść za pomocą składni 1974-1/6_AD, aby kolejność oceny operatora działała, ale jest niezgrabna.

Więc moje pytanie sprowadza się do tego, czy uważasz, że ta cecha będzie się usprawiedliwiać? Jakie inne literały chciałbyś zdefiniować, które sprawią, że Twój kod C++ będzie bardziej czytelne?


Zaktualizowano składnię, aby pasowała do ostatecznego projektu w czerwcu 2011

Author: hichris123, 2008-10-26

12 answers

Oto przypadek, w którym istnieje korzyść z używania liter 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 run-time jest konwertowany na błąd w czasie kompilacji. Nie można dodać static assert do bitset ctor biorąc łańcuch znaków (przynajmniej nie bez argumentów szablonu łańcuchów).

 67
Author: emsr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-29 13:16:19

Na pierwszy rzut oka wydaje się być prostym cukrem składniowym.

Ale patrząc głębiej, widzimy, że to coś więcej niż cukier składniowy, ponieważ rozszerza opcje użytkownika C++, aby tworzyć typy zdefiniowane przez Użytkownika, które zachowują się dokładnie tak, jak odrębne typy wbudowane. Ten mały "bonus" jest bardzo interesującym dodatkiem do C++11.

Czy naprawdę potrzebujemy go w C++?

Widzę kilka zastosowań w kodzie, który napisałem w ostatnich latach, ale to, że go nie używałem w C++ nie znaczy, że nie jest to interesujące dla innego programisty C++ .

Używaliśmy w C++ (i chyba w C), literałach zdefiniowanych przez kompilator, do wpisywania liczb całkowitych jako krótkich lub długich liczb całkowitych, liczb rzeczywistych jako zmiennoprzecinkowych lub podwójnych( lub nawet długich podwójnych) oraz ciągów znaków jako zwykłych lub szerokich znaków.

W C++ mieliśmy możliwość tworzenia własnych typów (tj. klas), potencjalnie bez napowietrznych (inlining, itp.). Mieliśmy możliwość dodawania operatorów do ich typów, aby je zachowuj się jak podobne wbudowane typy, co pozwala programistom C++ używać macierzy i liczb złożonych tak naturalnie, jak gdyby zostały one dodane do samego języka. Możemy nawet dodać operatory obsady (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 wydaje mi się, że to naturalna ewolucja języka, ale być tak kompletnym, jak possible: "Jeśli chcesz utworzyć typ i chcesz, aby zachowywał się jak najwięcej jak typy wbudowane, oto narzędzia..."

Domyślam się, że jest to bardzo podobne do decyzji. NET o uczynieniu każdej prymitywnej struktury, w tym booleanów, liczb całkowitych itp., a wszystkie struktury wywodzą się z obiektu. Sama ta decyzja stawia. NET daleko poza zasięg Javy podczas pracy z prymitywami, bez względu na to, jak wiele hacków boksujących/rozpakowujących Java doda do swojej specyfikacji.

Czy naprawdę potrzebujesz go w C++?

To pytanie jest dla Ciebie , aby odpowiedzieć. Nie Bjarne Stroustrup. Nie Herb Sutter. Nie jest członkiem C++ standard committee. Dlatego masz wybór w C++ i nie będą one ograniczać użytecznej notacji tylko do typów wbudowanych.

Jeśli potrzebujesz go, to jest to mile widziany dodatek. Jeśli ty nie, cóż... Nie używaj go. Nic cię to nie będzie kosztowało.

Witamy w C++, języku, w którym funkcje są opcjonalnie.

Wzdęty??? Pokaż mi swoje kompleksy!!!

Istnieje różnica między nadętym i złożonym (kalambur zamierzony).

Jak pokazał Niels w jakie nowe możliwości dodają literały zdefiniowane przez Użytkownika do C++?Jest to jedna z dwóch cech dodanych "ostatnio" do C i C++:, możliwość zapisu liczby zespolonej jest jedną z dwóch cech 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 C99 typu "double complex", jak i C++ Typu "STD:: complex" można mnożyć, dodawać, odejmować itp., za pomocą operatora przeciążenie.

Ale w C99 dodali kolejny typ jako Typ wbudowany, A wbudowany operator przeciąża obsługę. I dodali kolejną wbudowaną funkcję dosłowną.

W C++ po prostu używali istniejących cech języka, widzieli, że literalna cecha jest naturalną ewolucją języka i dlatego ją dodali.

W C, jeśli potrzebujesz tego samego rozszerzenia notacji dla innego typu, masz pecha do czasu lobbingu, aby dodać swoje funkcje fal kwantowych (lub 3D punkty, czy jakikolwiek podstawowy typ używasz w swojej dziedzinie pracy) do standardu C jako Typ wbudowany.

W C++11 możesz to zrobić sam:

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

Jest nadęty? Nie , potrzeba jest tam, jak pokazano w jaki sposób zarówno c jak i C++ kompleksy potrzebują sposobu, aby reprezentować swoje dosłowne złożone wartości.

Czy jest źle zaprojektowany? Nie , jest zaprojektowany jak każda inna funkcja C++, z myślą o rozszerzalności.

Czy to tylko do celów notacji? Nie , ponieważ może nawet dodać bezpieczeństwo typu do kodu.

Na przykład, wyobraźmy sobie kod zorientowany 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 !

Bardzo łatwo jest wymusić silne typowanie przy przypisywaniu wartości.

Czy jest niebezpieczny? Dobre pytanie. Czy te funkcje mogą być przestrzenią nazw? Jeśli tak, to Jackpot!

W każdym razie, Jak wszystko, możesz się zabić, jeśli narzędzie jest używane niewłaściwie . C jest potężny i możesz odstrzelić sobie głowę, jeśli użyjesz pistoletu C. C++ ma Pistolet C, ale także skalpel, paralizator i inne narzędzia, które znajdziesz w zestawie. Możesz użyć skalpela i wykrwawić się na śmierć. Możesz też zbudować bardzo elegancki i solidny kod.

Więc, jak każda funkcja C++, naprawdę jej potrzebujesz? Jest to pytanie, na które musisz odpowiedzieć przed użyciem go w C++. Jeśli tego nie zrobisz, nic cię to nie będzie kosztowało. Ale jeśli naprawdę tego potrzebujesz, przynajmniej język cię nie zawiedzie.

Przykład daty?

Twój błąd, wydaje się ja, to że mieszasz operatory:

1974/01/06AD
    ^  ^  ^

Tego nie da się uniknąć, ponieważ / będąc operatorem, kompilator musi go zinterpretować. AFAIK, to dobrze.

Aby znaleźć rozwiązanie twojego problemu, napisałbym dosłowne 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. Co jest celem pozwalającym użytkownikowi definiować własne dosłowne nazwy.

 185
Author: paercebal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:53

To bardzo miłe dla kodu matematycznego. Z mojego umysłu widzę zastosowanie dla następujących operatorów:

Stopień 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 z niego korzystać. Pomagają one uczynić stałe w kodzie bardziej czytelnymi. To kolejne narzędzie, które sprawia, że kod jest nieczytelny, ale mamy już tak wiele narzędzi nadużycia, że jeden więcej nie boli wiele.

 34
Author: Nils Pipenbrinck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-15 19:19:57

UDL są przestrzenią nazw (i mogą być importowane za pomocą deklaracji/dyrektyw, ale nie można jawnie przestrzeni nazw literalnie jak 3.14std::i), co oznacza ,że (miejmy nadzieję) nie będzie mnóstwo starć.

Fakt, że mogą być faktycznie template (i constexpr ' D) oznacza, że możesz zrobić całkiem potężne rzeczy z UDLs. Autorzy biginta będą bardzo zadowoleni, ponieważ mogą w końcu mieć dowolnie duże stałe, obliczane w czasie kompilacji (przez constexpr lub szablony).

I ' m szkoda tylko, że nie zobaczymy kilku przydatnych liter w standardzie (z wyglądu), jak s dla std::string i i dla jednostki urojonej.

Ilość czasu kodowania, który zostanie zaoszczędzony przez UDLs, nie jest tak wysoka, ale czytelność zostanie znacznie zwiększona i coraz więcej obliczeń może zostać przesuniętych na czas kompilacji w celu szybszego wykonania.

 16
Author: coppro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-05 23:07:24

Pozwól, że dodam trochę kontekstu. Do 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 jest w stanie zdefiniować elementy modelu jako klasy w C++. Przyjmujemy podejście przekształcania metamodelu (Ecore) do szablonów z argumentami. Argumentami szablonu są strukturalne charakterystyka typów i klas. Na przykład, klasa z dwoma atrybutami int byłaby czymś w stylu:

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

Hoever, okazuje się, że każdy element w modelu lub metamodelu, zwykle ma swoją 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ż łańcuchy znaków są zabronione jako argumenty szablonów. Możesz napisać nazwę char po char, ale to jest co prawda bałagan. Z odpowiednimi literałami zdefiniowanymi przez użytkownika, moglibyśmy napisać coś podobnego. Powiedzmy, że używamy "_n" do identyfikacja nazw elementów modelu (nie używam dokładnej składni, tylko po to, aby stworzyć pomysł):

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

Wreszcie, posiadanie tych definicji jako szablonów pomaga nam w projektowaniu algorytmów do przechodzenia elementów modelu, przekształceń modelu itp. które są naprawdę wydajne, ponieważ Typ informacji, identyfikacji, przekształceń, itp. są określane przez kompilator w czasie kompilacji.

 12
Author: Diego Sevilla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-29 12:40:27

Bjarne Stroustrup mówi o UDL w tym C++11 talk , w pierwszej sekcji o interfejsach bogatych w typy, około 20 minut.

Jego podstawowy argument dla UDLs przybiera formę sylogizmu:

  1. "trywialne" typy, czyli wbudowane typy prymitywne, mogą wychwytywać tylko trywialne błędy typu. Interfejsy z bogatszymi typami pozwalają systemowi typów wychwycić więcej rodzajów błędów.

  2. Rodzaje błędów typu, które mogą wychwycić kod, mają wpływ na prawdziwy kod. (Podaje przykład orbitera Marsa, który zawiódł z powodu błędu wymiarów w ważnej stałej).

  3. W kodzie rzeczywistym jednostki są rzadko używane. Ludzie ich nie używają, ponieważ nakładanie na siebie nakładów obliczeniowych lub pamięci w celu tworzenia bogatych typów jest zbyt kosztowne, a używanie wcześniej istniejącego kodu jednostki szablonowej C++ jest tak brzydkie, że nikt go nie używa. (Empirycznie nikt go nie używa, mimo że biblioteki są już od dekada).

  4. Dlatego, aby skłonić inżynierów do użycia jednostek w rzeczywistym kodzie, potrzebowaliśmy urządzenia, które (1) nie ponosi kosztów związanych z uruchomieniem, a (2) nie jest akceptowalne.

 10
Author: masonk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-09 00:10:00

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

Patrz na przykład PhysUnits-ct-Cpp11 , mała biblioteka C++11, C++14 tylko nagłówek do analizy wymiarowej w czasie kompilacji i manipulacji jednostek/ilości i konwersji. Prostsze niż Boost.Jednostki , Obsługuje symbol jednostki literały takie jak M, g, s, przedrostki metryczne takie jak m, k, M, zależy tylko od standardowej biblioteki C++, si-only, potęgi całkowe wymiary.

 8
Author: Martin Moene,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-02 20:07:56

Hmm... Nie myślałem jeszcze o tej funkcji. Twoja próbka była dobrze przemyślana i z pewnością jest interesująca. C++ jest bardzo potężny, jak to jest teraz, ale niestety składnia używana w kawałkach kodu, które czytasz, jest czasami zbyt złożona. Czytelność to, jeśli nie wszystko, to przynajmniej dużo. A taka funkcja byłaby nastawiona na większą czytelność. If I take your last example

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

... Zastanawiam się, jak byś to dzisiaj wyraził. Mielibyście klasę KG i LB i porównalibyście obiekty:

assert(KG(1.0f) == LB(2.2f));
I to też by się przydało. W przypadku typów o dłuższych nazwach lub typów, które nie mają nadziei na posiadanie tak ładnego konstruktora do bezpisywania adaptera, może to być miły dodatek do tworzenia i inicjalizacji obiektów w locie. Z drugiej strony można już tworzyć i inicjować obiekty za pomocą metod. Ale Zgadzam się z Nilsem w matematyce. Funkcje trygonometryczne C i C++ wymagają na przykład wprowadzania w radianach. Myślę, że w stopniach chociaż bardzo krótka, ukryta konwersja, taka jak Nils, jest bardzo miła.

Ostatecznie będzie to jednak cukier składniowy, ale będzie to miało niewielki wpływ na czytelność. Prawdopodobnie łatwiej będzie też napisać niektóre wyrażenia(sin(180.0 deg) jest łatwiejszy do napisania niż sin(deg (180.0)). A potem pojawią się ludzie, którzy nadużywają tej koncepcji. Ale wtedy, ludzie obelżywi językowo powinni używać bardzo restrykcyjnych języków, a nie czegoś tak ekspresyjnego jak C++.

Ach, mój post mówi w zasadzie nic poza: będzie dobrze, wpływ nie będzie zbyt duży. Nie martwmy się. :-)
 6
Author: mstrobl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-26 10:31:52

Nigdy nie potrzebowałem ani nie chciałem tej funkcji (ale to może być efekt Blub). Moja reakcja na szarpnięcie kolana jest taka, że jest kulawa i prawdopodobnie spodoba się tym samym ludziom, którzy uważają, że fajnie jest przeciążać operatora+ za każdą operację, która może być zdalnie interpretowana jako dodanie.

 3
Author: fizzer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-26 10:08:13

C++ jest zwykle bardzo surowy co do używanej składni - poza preprocesorem niewiele można użyć do zdefiniowania niestandardowej składni/gramatyki. Np. możemy przeciążać istniejące operacje, ale nie możemy definiować nowych-IMO jest to bardzo zgodne z duchem C++.

Nie przeszkadza mi kilka sposobów na bardziej spersonalizowany kod źródłowy - ale wybrany punkt wydaje mi się bardzo odizolowany, co najbardziej mnie myli.

Nawet zamierzone użycie może znacznie utrudnić odczytanie kodu źródłowego: an pojedyncza litera może mieć rozległe skutki uboczne, których w żaden sposób nie można zidentyfikować z kontekstu. Z symetrią do u, l I f, większość programistów wybierze pojedyncze litery.

Może to również sprawić, że scopowanie stanie się problemem, używanie pojedynczych liter w globalnej przestrzeni nazw będzie prawdopodobnie uważane za złą praktykę, a narzędzia, które mają ułatwiać mieszanie bibliotek (przestrzenie nazw i identyfikatory opisowe), prawdopodobnie pokrzyżują jego cel.

Widzę pewne zasługi w połączeniu z " auto", również w połączeniu z biblioteką jednostek, jak boost units , ale nie na tyle, aby zasłużyć na ten warunek.

Zastanawiam się jednak, jakie sprytne pomysły wymyślamy.

 2
Author: peterchen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-26 19:44:30

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

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

Użycie konstruktora std::string(str, n), Aby \0 nie przeciął łańcucha na pół. (Projekt wykonuje wiele pracy z różnymi formatami plików.)

To było pomocne również, gdy porzuciłem std::string na rzecz opakowania dla std::vector.
 1
Author: rr-,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-27 13:24:22

Hałas w tym czymś jest ogromny. Również jest to straszne czytać.

Daj mi znać, czy oni uzasadnić, że nowa składnia dodanie z jakiegokolwiek rodzaju przykładów? 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żyłbyś składni i w 1000 innych wierszach. Jeśli piszesz, prawdopodobnie piszesz 10000 linijek czegoś innego. Szczególnie kiedy będziesz nadal prawdopodobnie pisać głównie wszędzie to:

std::complex<double> val = 3.14i

'auto' - słowo kluczowe może być jednak uzasadnione, tylko być może. Ale weźmy tylko C++, ponieważ jest lepszy niż C++0x w tym aspekcie.

std::complex<double> val = std::complex(0, 3.14);
To tak.. to proste. Nawet myślałem, że wszystkie STD i spiczaste nawiasy są po prostu kiepskie, jeśli używasz go wszędzie. Nie zaczynam zgadywać jaka jest składnia w C++0x do zamiany STD:: complex pod complex.
complex = std::complex<double>;

To może coś prostego, ale ja nie wierz, że 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 to: pisanie 3.14 i zamiast STD:: complex(0, 3.14); ogólnie nie oszczędza Ci dużo czasu, z wyjątkiem kilku super specjalnych przypadków.

 -5
Author: Cheery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-15 19:22:27