Portál AbcLinuxu, 20. května 2024 14:22
Dnes se podíváme zase trochu dál. Nejprve se mrkneme na datové typy v D, pak nějaké ty proměnné, základní matematiku a práci s funkcemi.
Abychom mohli pokračovat dále, je třeba se naučit něco o typech. Na znalosti typů závisí pak proměnné, které jsou jedním z nejdůležitějších prvků jazyka. Typový systém D je o něco jednodušší než ten z jazyka C nebo C++, ale neztrácí nic na síle. Tak, jdeme na to
Každá hodnota musí mít nějaký typ, který reprezentuje, jak se taková hodnota bude chovat a jak je velká. K tomu se využívají datové typy. Ty si můžeme roztřídit na několik kategorií.
Nejprve se podíváme na základní
(atomické) typy, tzn. ty na
úplně
nejnižší
úrovni. Mezi ty patří celá čísla,
reálná čísla, boolovské hodnoty,
komplexní čísla a znaky. Kromě toho by se dal do této kategorie
zařadit
i typ void
, jakožto "prázdno" - v jazyce se také často
využívá a nemá
žádnou hodnotu.
D je staticky typovaný jazyk. To znamená, že kontrola typů probíhá při kompilaci, ne za běhu. To má za následek, že se různé hodnoty budou mít stále stejný typ, s jakým byly definovány. Takový jazyk je potom obvykle striktnější, ale zase se programátor vyvaruje typovým chybám za běhu.
Základní typy vždy mají nějakou velikost (kromě void
).
Ta se značí v
bitech (popř. bytech). Vyjadřuje, kolik místa v
paměti hodnota určitého
typu zabere. Teď si můžete říct "noj o, ale není to plýtvání, mít typ,
který pobere např. velké číselné hodnoty a místo nevyužít?". Ano,
samozřejmě. Ale jazyk nemá jen jeden číselný typ. Místo toho jich
existuje celá řada, s různými rozsahy hodnot.
Puristé budou protestovat, že "to přece nejsou vůbec celá čísla",
ale
počítejme s tím, že jsou. D definuje několik různě velkých
celočíselných (integrálních) typů, které jsou často analogické s těmi v
jazyce C. Základní, se kterým budeme pracovat, je int
. Int
je zkratka
pro integer. V jazyce C není
jeho velikost definována standardem. V
D ano - int
je vždy 32
bitů velký a zahrnuje jak záporná, tak
kladná čísla.
Pokud jsou třeba jen čísla kladná (nula a větší), tak pro tyto účely je
přítomen typ uint
(v C by se zapsal jako unsigned
int
, tzn.
"bezznaménkový int"). Ten může logicky jít více do kladných hodnot, než
standardní int
, ale je useklý v záporných hodnotách.
Vždy není vhodné pracovat s tak velkými čísly. Proto D poskytuje ještě
dva menší typy (oba mají i své unsigned protějšky, jen s předponou
u
). Těmi jsou short
a byte
.
Typ short
má vždy 16 bitů. Analogicky
typ byte
má velikost přesně jeden byte.
Stejně tak jsou
občas potřeba i větší hodnoty než jen 32 bitů. Ty reprezentují typy
long
(vždy 64 bitů, unsigned verze ulong
) a cent
(vždy 128 bitů,
ucent
pro unsigned, ale tento typ není ještě implementován
a je jen
rezervován pro pozdější užití).
Cčkaři vědí, že hodnoty různých typů se automaticky neinicializují a je
třeba explicitně specifikovat hodnotu. Toto neplatí v případě D.
Všechny integrální typy jsou implicitně 0
.
Občas celá čísla nestačí. Stejně jako pro celá čísla, D poskytuje několik typů s desetinnou čárkou.
Základním typem, se kterým se pracuje, je float
(od floating point).
Float má, analogicky k typu int
velikost 32 bitů (v Cčku
nedefinovaná). Menší neexistuje. 64bitový float
se
jmenuje double
(od "double precision" - dvojitá přesnost; velikost v Cčku logicky není
definována). Poslední typ real
nemá definovanou
velikost.
Představuje největší floating point číslo reprezentovatelné hardwarem
(80 bitů pro x86).
Výchozí hodnota je u těchto typů vždy TYP.nan
.
D má separátní typ bool
, který může reprezentovat vždy
jen 2
hodnoty
(je tudíž v podstatě jedním bitem). Tyto hodnoty se jmenují false
a
true
("pravda" a "nepravda"). Výchozí hodnotou je vždy false
.
Bool
hodnoty jsou implicitně konvertovatelné na jakýkoliv integrální typ
(konverze - viz další lekce). Při konverzi se false
stane
0 a true
1. Klasické aritmetické operátory se na bool hodnotách nepoužívají.
Bitové operátory, AND/OR a ternární operátor se s bool
hodnotami použít
dají.
D má zabudovaná komplexní čísla, která jsou analogická k floating
point
typům. Jedninou změnou je, že mají předponu c
(cfloat
,
cdouble
,
creal
). Protože komplexní čísla se skládají ze dvou
složek, je třeba
nějak reprezentovat tu imaginární složku. K tomu slouží typy ifloat
,
idouble
a ireal
. Výchozí hodnoty komplexních
typů se také liší.
Jsou vždy TYP.nan + TYP.nan * 1.0i
(např. float.nan
+ float.nan *
1.0i
).
Tyto typy jsou zajímavé tím, že jejich velikosti v podstatě kopírují
ty
celých čísel. Znakové typy jsou tři. Jmenují se char
,
wchar
a
dchar
. Všechny jsou integrální a unsigned. Typ char
má velikost 8
bitů (tudíž kopíruje ubyte
). Typ wchar
má
16 bitů (jako ushort
).
A dchar
má 32 bitů (jako uint
). Rozdíl je v
jejich výchozích
hodnotách.
Znakové typy reprezentují jednotky unicode. Typ char
je
jedna UTF-8 jednotka. Analogicky, wchar
reprezentuje
UTF-16 a dchar
UTF-32. Jejich výchozí hodnoty nejsou 0
ale 0xFF
(pro char
),
0xFFFF
(pro wchar
) a 0x0000FFFF
(pro dchar
).
Je jasné, že jen se základními atomickými typy si programátor nevystačí. D poskytuje několik odvozených typů. Základním odvozeným typem je pointer (ukazatel). Pointerů se hojně využívá v jazycích C a C++. V D ani nejsou pro vysokoúrovňové programování moc potřeba, ale D je samozřejmě univerzálním jazykem.
Pointer není nic jiného, než číselná
hodnota. Jeho velikost je
definována platformou. Na většině *nix systémů je pod x86 velký 32 bitů
a pod x86_64 64 bitů. Stejně velký jako pointer je typ size_t
,
což
je alias k číselnému typu stejně velkému jako pointer. Číselná hodnota
pointeru reprezentuje adresu v paměti.
Vzhledem k tomu, že pointer je
číslem, tak se k němu dá přičítat a odečítat, a tak měnit, kam v paměti
pointer ukazuje. Rozdíl dvou pointerů je typ ptrdiff_t
,
který je
stejně velký jako size_t
a je také aliasem, ale narozdíl
od něj je
znaménkový.
O pointerech se dál zmíním v pozdějších lekcích, teď je ještě brzy.
Dalším odvozeným typem v D je pole. Pole mohou být dvou typů. Statická pole se alokují na zásobníku (stack) a mají předem určenou délku. Je to v podstatě posloupnost hodnot určitého typu v paměti (jdou hned po sobě). Spousta lidí si plete v jazyce C pointery a pole. Jsou to ale dosti odlišné věci. Pointer se ale může chovat jako pole a může na něj ukazovat. V takovém případě pointer ukazuje na první hodnotu v poli. Vzhledem k tomu, že takové hodnoty jdou po sobě, tak přičítáním k pointeru je možné získat hodnotu na určitém indexu v poli (ale opět, o tom později, tohle už je pokročilejší látka).
Dynamická pole v D jsou alokována na haldě (heap).
Také je to posloupnost
hodnot, ale vzhledem k tomu, že není na fixním stacku, tak je možné
měnit délku takového pole. I na dynamické pole může ukazovat pointer.
Pro uživatele C++, dynamické pole v D má podobné použití jako vector
v C++, ale je lépe zabudované do jazyka.
Každá hodnota v poli má svůj index
(číslo, první hodnota má index 0
,
poslední DÉLKA-1
). Velikosti polí v paměti (neplést s
délkou pole!) je
DÉLKA*VELIKOST_HODNOTY
.
Kromě toho má D i asociativní pole. Asociativní pole nemají indexy. Místo toho má každá hodnota klíč, který může být různých typů (např. řetězec). Správně bych je neměl do této kategorie řadit, ale sedí tu. Řadit bych je neměl sem proto, že jsou uvnitř implementované jako hash tabulky, a tudíž jsou objektem, ne odvozeným typem od atomického typu.
Existují ještě dva další odvozené typy. Těmi jsou funkce a delegáty. Funkce reprezentují blok kódu - kontext funkce (scope). Funkce mohou manipulovat s hodnotami ve vlastním kontextu a s globálními hodnotami. Lokální hodnoty jiných funkcí (např. main) musí být poslány přes argumenty (o těch v další lekci). Funkce mají vlastní speciální typ pointerů (tzv. function pointery). Konverze mezi normálními pointery a FP znamená nedefinované chování (resp. definované platformou) ale většinou funguje (občas se toho i dost ošklivě využívá). Delegáty se jinak jmenují "fat pointer" ("tlustý pointer"). Jsou složeninou dvou pointerů. Jeden ukazuje na kontext funkce, ve které byl delegát definován (využívají se většinou jako lambda funkce, o této problematice později) a druhý na funkci samotnou (function pointer). Delegátům bude později věnována vlastní lekce.
To ale stále nestačí. Kromě předchozích kategorií, které se dají společně pojmenovat jako POD D má ještě uživatelské typy.
Nejjednodušším je alias
. Alias je jen jiný název pro
některý předtím
definovaný typ. Při kompilaci se takový alias expanduje na plný název
typu.
Dalším uživatelským typem je enumerace
(enum
). Je to v
podstatě
kolekce konstant. Později s
nimi budeme pracovat.
Struktury (struct
) a třídy (class
), další dva
uživatelské typy,
nejsou v jazyce C++ rozlišeny. V D ano, struktury jsou hodnotové typy,
třídy referenční typy (o
referencích opět později). Posledním
uživatelským typem je union
. To je typ, který definuje
několik hodnot
různých typů, ale naráz může obsahovat pouze jeden (všechny typy v
union
sdílí jednu část paměti).
Všemi uživatelskými typy se budeme zabývat v pozdějších kapitolách podrobně.
Zde bych skončil. Přejdeme na proměnné a už i napíšeme nějaký ten kód.
V předchozích lekcích jsme psali nějaké ty úvodní aplikace. Programování ale není pouze o vypisování "hello world" do konzole. Hodnoty si musíme i nějak ukládat a s nimi dále pracovat. K tomuto účelu musíme zavést tzv. proměnné.
Řekněme si, že máme číslo 150
. Toto číslo chceme vypsat
třikrát po
sobě. Přitom po změně tohoto čísla na jednom místě se změní i výstup
programu. K tomuto účelu si zavedeme proměnnou. Proměnné jsou jakési
"pojmenované instance typů". Zapíšeme je takto:
TYP název;
Např. pro typ int
zapíšeme:
int cislo;
Tomuto se říká deklarace proměnné. Proměnné se nikdy nesmí jmenovat jako nějaké rezervované klíčové slovo jazyka. Název proměnné (identifikátor) musí být vždy unikátní vzhledem k aktuálnímu kontextu, jinak dojde ke kolizi a program se nezkompiluje.
No jo, ale my chceme, aby proměnná měla i hodnotu. Deklarované proměnné
ji tedy přiřadíme pomocí operátoru =
. To se jmenuje definice
proměnné.
cislo = 150;
Deklarace i definice se dají kombinovat.
int x = 5;Proměnné, která už má hodnotu, se dá přiřadit jiná hodnota.
int x = 5; x = 10;
Hodnota jiné proměnné se dá přiřadit také:
int x = 5; int y = x;
Obě proměnné budou poté mít nezávislou hodnotu (kopii) (kromě non-POD typů).
Jak už jsem předtím zmínil, názvy proměnných musí být unikátní. Pro pochopení je nutné zavést pojmy globální proměnná a lokální proměnná. Globální proměnná je jakákoliv proměnná deklarovaná mimo kontext určité funkce (popř. třídy, struktury). Vezměme takový program:
import std.stdio; /* dvě globální proměnné, přístupné odkudkoliv */ int foo = 150; string bar = "baz"; void main() { /* lokální proměnná - lokální proměnné nikdy nekolidují s globálními */ string bar = "bah"; short baz = 65536; /* vypíše "150bah65536" */ writeln(foo, bar, baz); }
Dobrá, máme lokální proměnnou, která se jmenuje stejně jako globální. Ale co když je třeba ve funkci s lokální proměnnou přistoupit ke globální, která má stejný název? To je jednoduché:
import std.stdio; int foo = 150; void main() { int foo = 250; writeln(foo, .foo); }
Přidáním tečky před název proměnné se zdůrazní, že se přistupuje ke globální proměnné, nikoliv k lokální.
V jazyce D se rozlišují dva typy konverzí mezi typy, tzv. implicitní konverze a explicitní konverze. Implicitní konverze znamená, že hodnota jednoho typu se dá hned přiřadit proměnné jiného typu. Implicitní konverze je možné provádět hlavně u číselných typů, z typů s menší velikostí na typy s větší velikostí. Např. tato konverze je povolena:
int foo = 5; long bar = foo;
Obráceně už to ale nejde:
long foo = 5; int bar = foo;
Explicitní konverze, tzv cast, samozřejmě funguje správně, pokud se hodnota větší proměnné vejde do menší:
int foo = 5; /* "bar" bude 5 */ short bar = cast(short) foo;
Ale ne, když se nevejde:
int foo = 65537; /* "bar" bude "foo - největší_hodnota_ushort - 1", tzn. 1 */ short bar = cast(short) foo;
Explicitní konverze podnulových hodnot na unsigned hodnoty není možná. Kromě toho není možné převádět implicitně:
Tímto bych skončil tuto lekci. Látku budeme dále rozšiřovat s rozšiřujícími se znalostmi.
1) Napište program, který vytvoří proměnnou typu short s hodnotou 16 a
z ní udělá int, long, ushort, uint, ulong
proměnné a
vypíše je.
2) Vytvořte proměnnou typu long s hodnotou 10 a převeďte ji explicitně
na int, short, ulong, uint, ushort
a přesvědčte se, že
hodnota zůstává
zachována.
Čísla v D samozřejmě můžeme přičítat, odečítat, násobit, dělit, apod., stejně jako v ostatních programovacích jazycích. Ale nebudeme vždy pracovat jen s čísly. Abychom mohli začít dělat i něco s jinými typy, musíme definovat literály. Pak napíšeme i nějaký další kód.
Na hodnotách a proměnných můžeme provádět klasické aritmetické operace. Např. součet dvou čísel:
int foo = 5 + 10;
Samozřejmě, toto není moc užitečné. Stejně můžeme ale manipulovat s proměnnými:
int foo = 5; int bar = foo + 10;
Přičtení hodnoty k proměnné je tedy logicky:
int foo = 5; foo = foo + 10;
To je ale trochu zdlouhavé, takže se to dá skloubit s přiřazením:
int foo = 5; foo += 10;
Stejně tak můžeme odečítat, násobit a dělit.
Pro přičtení, resp. odečtení hodnoty 1
se dají použít i
operátory ++
a --
. Ty se dají zapsat buď před proměnnou nebo za
proměnnou.
int foo = 5; foo++; ++foo; foo--; --foo;
Proměnná foo
bude mít na konci opět hodnotu 5. Jaký je
v tom ale
potom rozdíl?
Při zapsání foo++
, tzv. post-inkrementaci, se přičte 1 a
vrátí se
hodnota před přičtením. Tudíž zápis:
int foo = 5; int bar = foo++;
vyjádří, že foo
na konci bude 6, ale bar
bude 5.
U ++foo
, tzv. pre-inkrementace, je to ale jinak:
/* foo i bar budou 6 */ int foo = 5; int bar = ++foo;
Stejná pravidla platí i pro --
.
Zbytek po celočíselném dělení získáme operátorem %
. Např.
zápis:
int foo = 11; writeln(foo % 3);
vypíše 2 (protože 10/3 se celočíselně dělit nedá, 9 ano, zbytek do 9 je 1). Ostatní operace jsou pak definovány knihovnou.
Při počítání platí určitá pravidla o výsledném typu. Popíšu je, jak jdou po sobě.
real
, tak se druhý
zkonvertuje
také na real
.
double
, tak se druhý
zkonvertuje
také na double
.
float
, tak se druhý
zkonvertuje
také na float
.
- bool => int - byte => int - ubyte => int - short => int - ushort => int - char => int - wchar => int - dchar => uint
Pro enum typy platí další pravidla. Těmi se budeme zabývat později.
Literály lze definovat jako prvky s hodnotou. Mohou být různých typů. My už použili číselné literály a řetězcové literály. Typů literálů je samozřejmě více.
D má několik typů číselných literálů. Základním je samozřejmě
klasické
celé číslo. Patří k němu typ int
. Literály lze dle
velikosti
přiřazovat k jakémukoliv celočíselnému typu. Zapíšeme jej takto:
int x = 5; /* 5 je samotný literál */
Lze zapsat i hexadecimální literály:
int x = 0xDEADBEEF;
Oktalové literály už nejsou podporovány. Místo nich se dají využít funkce standardní knihovny.
Pro unsigned čísla pouze přidáme koncovku u
popř U
.
uint x = 5u;
Pro literály typu long
přidáme l
nebo L
.
Unsigned long se pak
zapíše s ul
, popř. UL
. Float literály končí
s f
, popř. F
:
float a = 3.14f;
Bez koncovky je výchozím typem float literálů typ double
.
Implicitní
konverze je nicméně možná, takže zápis
float a = 3.14;
je možné stejně jako s double
. Literály typu real
mají koncovku L
nebo l
:
real a = 3.14L;Je lepší v takových případech používat raději velké
L
,
protože malé se
snadno splete s jedničkou.
Základním řetězcovým literálem je prostě řetězec uzavřený v dvojitých uvozovkách. Zapisuje se takto:
string a = "hello world";
Dva literály hned po sobě se chovají jako jeden:
string a = "hello " "world";
Takovéto literály mohou obsahovat escape sekvence, které se pak
převedou na jejich pravé reprezentace. Tomu se dá zamezit použitím
WYSIWYG literálů, které se zapisují stejně, jen s předponou r
:
string a = r"všechno\se\bude\zobrazovat\njak\píšu\r\t";
Tyto literály mají i alternativní syntaxi:
string a = `toto ta\ky fun\guje\`;
Dalším typem jsou hexadecimální řetězcové literály:
string a = x"0A"; /* jako "\x0A" */
Mezery a nové řádky jsou ignorovány. Počet hex čísel musí být násobkem
dvou.
Všechny tři typy literálů mohou mít postfix znak, který definuje jejich
typ. Normálně je to string
, což je alias k immutable(char)[]
(pole
imutabilních znaků). Takové pole se skládá z UTF-8 jednotek. Pokud
chceme UTF-16, zapíšeme:
wstring a = "utf-16 řetězec"w;
To samé pro UTF-32:
dstring a = "utf-32 řetězec"d;
Dá se specifikovat i c
, což vyústí v string
(je tudíž implicitně
výchozím nastavením).
Poslední dva typy literálů jsou tzv. delimited literály a token
literály. Delimited literály jsou podobné WYSIWYG, ale umožňují zvolit
dva znaky, mezi kterými může být napsáno cokoliv a nebude to mít žádný
efekt. Tyto znaky musí být napsány hned před/po "
a před
"..." je třeba
napsat q
:
string a = q"{hello world\blah/"bah"`na rozdíl od wysiwyg fungují i tyto znaky`````}";
Delimitery mohou být páry [], ()
, <>
nebo {}
. Delimited stringy
se dají taky použít pro jednoduché zapsání více řádků:
writeln(q"EOS První řádek Druhý řádek Třetí řádek EOS" );
Token literály musí obsahovat syntakticky validní D kód (nemusí být
ale
sémanticky validní). Mají využití např. pro string mixiny (později se
jimi budeme zabývat). Textové editory je považují za kus D kódu, takže
mohou mít zvýrazněnou syntaxi. Zapisují se mezi q{}
:
string x = q{ writeln("hello world"); }
Znakové literály se zapisují mezi '
, např:
char x = 'F';
Dalšími dvěma typy jsou funkční literály (o těch v další lekci) a delegátové literály (někdy příště). Kromě toho existují i literály polí a asociativních polí. Polím bude věnována samostatná lekce. Tímto bych asi tuto ukončil.
Toto všechno je sice hezké, ale v reálných programech nemáme vše v
jedné funkci main
. Tudíž se naučíme s funkcemi pracovat.
V této kapitole nestihneme o funkcích všechno. Pokračovat budeme příští
týden, kdy se podíváme na přetěžování (overloading),
funkce s variabilním počtem
argumentů, vnořené funkce
a delegáty.
Pomocí funkcí můžeme program rozdělovat do přehledných struktur. Struktura funkcí vypadá nějak takto:
NÁVRATOVÝ_TYP název (parametr1, parametr2, parametr3, ...) { tělo_funkce }
Pak funkci můžeme zavolat:
název();
Příklad:
import std.stdio; void foo() { writeln("hello world"); } void main() { foo(); }
Pokud z funkce nevracíme hodnotu, pak návratový typ je vždy void
. Pro
návrat hodnoty z funkce
využíváme return
:
int foo() { return 5; }
Pokud potřebujeme vyskočit z funkce, která nic nevrací, zapíšeme prostě
return;
(to se hodí při praci s podmínkami).
Argumenty funkce můžeme číst stejně, jako proměnné:
int foo(int a, int b) { return a + b; } writeln(foo(5, 10));
Argumenty funkcí mohou mít různé atributy
(in, out, ref, lazy, const,
immutable, scope
), např:
void foo(ref int x, in int y, out int z, int q);
in
je ekvivalentní s const scope
.
scope
znamená, že reference v argumentu nemohou "utéct"
(tzn. zůstanou v
aktuálním kontextu).
out
nastaví hodnotu parametru na výchozí hodnotu typu.
Např. zápis
void foo(out int x) {} ... int a = 5; foo(a);
způsobí, že po zavolání foo
bude a rovno 0.
Pokud by funkce přiřadila
x
parametru nějakou hodnotu, tak se změní i venku:
void foo(out int x) { x = 150; } ... int a = 5; foo(a); /* a je teď 150 */
ref
funguje podobně, ale nedojde ke změně hodnoty
venku, pokud se
explicitně nezmění vevnitř. ref
je zkratkou k
"reference". Reference
je v podstatě "alias" k typu. Chová se jako standardní proměnná, ale
nemá žádnou vlastní hodnotu a místo toho ukazuje na cizí hodnotu
(reference jsou většinou implementované pomocí pointerů, ale jako
pointery se nechovají, protože není možné změnit hodnotu, na kterou
ukazují a chovají se jako "pravé" hodnoty, referencemi se budeme
zabývat později).
lazy
argumenty nejsou vyčísleny při zavolání funkce, ale
až ve
funkci. Tím pádem lze takový argument zavolat nula nebo vícekrát. Např:
void delej_neco_nekolikrat(int n, lazy void f) { while (n--) f(); } ... int x = 0; delej_neco_nekolikrat(5, writeln(x++));
vypíše do konzole:
0 1 2 3 4
const
znamená, že hodnota bude konstantní podle aktuální
reference
(ale jiná reference na stejnou paměť může). immutable
se
nemůže
měnit, je pouze pro čtení. Problematikou const / immutable
se budeme
zabývat později.
Pokud argumenty nemají žádné atributy, pak se stanou mutabilními, lokálními kopiemi. Problematika atributů souvisí s ostatními funkcemi jazyka, takže v průběhu seriálu by se měly veškeré pochybnosti, popř. nepochopení vyjasnit.
Funkce samotné mohou mít různé atributy také.
Pure funkce je taková funkce, která nemá žádné vedlejší efekty. Tyto funkce proto nemohou zapisovat globální proměnné, volat funkce, které nejsou pure, popř. provádět jakýkoliv vstup/výstup. Mohou ale alokovat paměť, ukončit program a pokud se jedná o debug kód, tak mít i vedlejší efekty. O těch více v kapitole o funkcionálním programování v D.
Nothrow funkce nevrací žádné výjimky odvozené z třídy Exception. Výjimkami se budeme zabývat později.
Analogicky k ref
parametrům funkce se může reference i
vracet.
Příklad:
int a = 5;
ref int foo() { return a; }
....
foo() = 10;
Hodnota globální proměnné pak bude 10 (reference mohou být tzv. lvalue, tzn. hodnotami "vlevo", ke kterým přiřazujeme).
Tyto funkce využívají tzv. typové inference ke zjištění typu návratové hodnoty. Příklad:
auto foo(int i) { return i * 2; } /* typ návratové hodnoty se automaticky nastaví na int */
Chovají se podobně jako kombinace ref funkcí a auto funkcí, s jedním rozdílem - automaticky si zjistí, jestli návratová hodnota je lvalue, a podle toho vrací buď referenci, nebo hodnotu.
auto ref foo(int a) { return a; } /* není lvalue, vrací hodnotu */ auto ref foo(ref int a) { return a; } /* vrací referenci */ auto ref foo(out int a) { return a; } /* vrací referenci */ auto ref foo() { return 150; } /* vrací hodnotu */
První return
výraz ve funkci rozhoduje, jestli se
vrací reference,
nebo hodnota.
Těmi se budeme zabývat v kapitole o const a imutabilitě. Vyjadřují jednotný zápis tří variant jedné funkce, kde první vrací mutabilní hodnotu, druhá konstantní hodnotu a třetí imutabilní hodnotu.
U některých funkcí chceme, abychom nemuseli specifikovat všechny argumenty, ale aby měla funkce nějaké výchozí hodnoty. To uděláme snadno:
int foo(int a, int b, string c = "blah") { writeln(a, b, c); }
Potom při zavolání
foo(5, 3);
program vypíše "53blah", ale při
foo(5, 3, "foo");
vypíše "53foo". Pokud má některý argument výchozí hodnotu, všechny následující musí mít logicky také výchozí hodnoty.
To by bylo pro dnešek asi vše. Příští týden budeme s funkcemi pokračovat a uděláme i nějaké nové věci (viz začátek lekce).
auto ref
funkcí.
auto
funkce.
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.