Sdílet prostřednictvím


Řetězec literál

Zpracování literálů řetězce se od spravovaných rozšíření jazyka C++ do Visual C++ 2010 změnilo.

V návrhu jazyka spravovaných rozšíření jazyka C++ byl spravovaný literál řetězce označen zahájením literálu řetězce pomocí S. Příklad:

String *ps1 = "hello";
String *ps2 = S"goodbye";

Výkonová režie mezi dvěma inicializacemi začíná být netriviální, jak ukazuje následující vyobrazení CIL prostřednictvím ildasm:

// String *ps1 = "hello";
ldsflda    valuetype $ArrayType$0xd61117dd
     modopt([Microsoft.VisualC]Microsoft.VisualC.IsConstModifier) 
     '?A0xbdde7aca.unnamed-global-0'

newobj instance void [mscorlib]System.String::.ctor(int8*)
stloc.0

// String *ps2 = S"goodbye";
ldstr      "goodbye"
stloc.0

To znamená znatelné úspory pouze za cenu zapamatování (nebo naučení) zadávání předpony S k literálu řetězce. V nové syntaxi je zpracování literálů řetězce transparentní, určeno kontextem použití. S již nemusí být určeno.

Co v případech, ve kterých potřebujeme explicitně nasměrovat kompilátor k jedné nebo druhé interpretaci? V těchto případech použijeme explicitní přetypování. Příklad:

f( safe_cast<String^>("ABC") );

Kromě toho literál řetězce nyní odpovídá String pomocí jednoduchého převodu místo standardního převodu. Zatímco toto nemusí znít jako velká změna, mění to rozlišení přetížené funkce, které zahrnuje String a const char* jako konkurenční formální parametry. Rozlišení, které bylo jednou rozpoznáno na instanci const char* je nyní označeno jako dvojznačné. Příklad:

ref struct R {
   void f(const char*);
   void f(String^);
};

int main () {
   R r;
   // old syntax: f( const char* );
   // new syntax: error: ambiguous
   r.f("ABC"); 
}

Proč je v tom rozdíl? Od doby kdy v programu existuje více než jedna instance s názvem f je vyžadován algoritmus pro rozlišení přetížení funkce, aplikovaný na volání. Formální rozlišení přetížené funkce zahrnuje tři kroky.

  1. Kolekce kandidátských funkcí. Kandidátské funkce jsou ty metody v rámci rozsahu, které se lexikálně shodují s názvem funkce, která byla vyvolána. Například když je f() vyvolána prostřednictvím instance R, všechny funkce s názvem f, které nejsou členy R (nebo její hierarchie základní třídy) nejsou kandidátské funkce. V našem příkladu jsou dvě kandidátské funkce. Jedná se o dvě členské funkce R s názvem f. V průběhu této fáze se volání nezdaří, pokud je sada kandidátských funkcí prázdná.

  2. Sada realizovatelných funkcí z kandidátských funkcí. Realizovatelná funkce je taková, kterou lze vyvolat s argumenty, zadanými ve volání, předáním počtu argumentů a jejich typů. V našem příkladu jsou obě kandidátské funkce také funkce realizovatelné. V průběhu této fáze se volání nezdaří, pokud je sada realizovatelných funkcí prázdná.

  3. Vyberte funkci, která představuje nejlepší shodu volání. To je provedeno hodnocením převodů, použitých ke transformaci argumentů na typ parametrů realizovatelné funkce. To je relativně jasné s funkcí s jedním parametrem; pokud existuje více parametrů, stává se to poněkud složitější. V průběhu této fáze se volání nezdaří, pokud není žádná nejlepší shoda. To znamená, pokud jsou převody, které jsou nezbytné pro převod typu skutečného argumentu na typ formálního parametru, stejně dobré. Volání je označeno jako dvojznačné.

Ve spravovaných rozšířeních rozlišení tohoto volání vyvolalo instanci const char* jako nejlepší shodu. V nové syntaxi je nyní převod, nezbytný pro shodu "abc" s const char* a String^ ekvivalentní – to znamená, je stejně dobrý – a proto je volání označeno jako chybné – to znamená, jako dvojznačné.

To nás přivádí na dvě otázky:

  • Jaký je typ skutečného argumentu "abc"?

  • Jaký je algoritmus pro stanovení kdy je jeden typ převodu lepší než jiný?

Typ řetězce literálů "abc" je const char[4] – nezapomeňte, že na konci každého řetězce literálů se nachází implicitní ukončovací znak null.

Algoritmus pro určení, kdy je lepší jeden typ převodu než jiný zahrnuje umístění možných převodů typů do hierarchie. Zde je mé porozumění hierarchii – všechny tyto převody jsou samozřejmě implicitní. Použití zápisu explicitního přetypování přepíše hierarchii podobným způsobem, jakým závorky přepíší obvyklou priority operátorů ve výrazu.

  1. Přesná shoda je nejlepší. Překvapením je, že aby byl argument přesnou shodou, nemusí přesně odpovídat typu parametru; pouze musí být dostatečně blízko. To je klíč k porozumění o co jde v tomto příkladu a jak byl jazyk změněn.

  2. Povýšení je lepší než standardní převod. Například povýšení short int na int je lepší než převod int na double.

  3. Standardní převod je lepší než převod zabalením. Například převod int na double je lepší než zabalení int do Object.

  4. Převod zabalením je lepší než implicitní převod definovaný uživatelem. Například zabalení int do Object je lepší než použití operátoru převodu třídy hodnoty SmallInt.

  5. Implicitní převod definovaný uživatelem je lepší než žádný převod. Implicitní převod definovaný uživatelem je posledním únikem před chybou (s výstrahou, že formální signatura může na této pozici obsahovat pole parametrů nebo tři tečky).

Znamená to tedy, že přesná shoda není nutně přesně odpovídající? Například const char[4] přesně neodpovídá const char* nebo String^ a zatím v nejednoznačnosti našeho příkladu je mezi dvěma konfliktními přesnými shodami!

Přesná shoda, jak se to stává, zahrnuje řadu triviálních převodů. Existují čtyři triviální převody pod ISO-C++, které mohou být použity a stále je možno to kvalifikovat jako přesnou shodu. Tři se nazývají transformace lvalue. Čtvrtý typ se nazývá převod kvalifikace. Tři transformace lvalue jsou považovány za lepší přesnou shodu než ta, vyžadují převod kvalifikace.

Jedna z forem transformace lvalue je převodem nativního pole na ukazatel. To je to, co se účastní ve shodě const char[4] s const char*. Proto je shoda mezi f("abc") a f(const char*) přesnou shodou. V dřívějších ztělesněních našeho jazyka to byla ve skutečnosti nejlepší shoda.

Proto kompilátor pro označení volání jako dvojznačného vyžaduje, aby byl převod const char[4] na String^ také přesnou shodou prostřednictvím triviálního převodu. Toto je změna, která byla zavedena v nové verzi jazyka. A to je důvod, proč je nyní volání označeno jako dvojznačné.

Viz také

Odkaz

System::String Handling in Visual C++

Koncepty

Obecné jazykové změny