Zoeken…
Opmerkingen
Het trefwoord auto
is een typenaam die een automatisch afgeleid type vertegenwoordigt.
Het was al een gereserveerd trefwoord in C ++ 98, geërfd van C. In oude versies van C ++ kon het expliciet worden vermeld dat een variabele automatische opslagduur heeft:
int main()
{
auto int i = 5; // removing auto has no effect
}
Die oude betekenis is nu verwijderd.
Standaard automatisch voorbeeld
Het trefwoord auto
biedt de automatische aftrek van het type van een variabele.
Het is vooral handig bij het omgaan met lange typenamen:
std::map< std::string, std::shared_ptr< Widget > > table;
// C++98
std::map< std::string, std::shared_ptr< Widget > >::iterator i = table.find( "42" );
// C++11/14/17
auto j = table.find( "42" );
met bereik-gebaseerd voor lussen :
vector<int> v = {0, 1, 2, 3, 4, 5};
for(auto n: v)
std::cout << n << ' ';
met lambdas :
auto f = [](){ std::cout << "lambda\n"; };
f();
om de herhaling van het type te voorkomen:
auto w = std::make_shared< Widget >();
om verrassende en onnodige kopieën te voorkomen:
auto myMap = std::map<int,float>();
myMap.emplace(1,3.14);
std::pair<int,float> const& firstPair2 = *myMap.begin(); // copy!
auto const& firstPair = *myMap.begin(); // no copy!
De reden voor de kopie is dat het geretourneerde type eigenlijk std::pair<const int,float>
!
automatische en expressiesjablonen
auto
kan ook problemen veroorzaken wanneer expressiesjablonen worden gebruikt:
auto mult(int c) {
return c * std::valarray<int>{1};
}
auto v = mult(3);
std::cout << v[0]; // some value that could be, but almost certainly is not, 3.
De reden is dat operator*
op valarray
u een proxy-object geeft dat naar de valarray
verwijst als een middel voor luie evaluatie. Door auto
, creëer je een bungelende referentie. In plaats van mult
had een std::valarray<int>
, dan zou de code zeker 3 afdrukken.
auto, const en referenties
Het auto
trefwoord zelf vertegenwoordigt een waardetype, vergelijkbaar met int
of char
. Het kan worden gewijzigd met het sleutelwoord const
en het &
-symbool om respectievelijk een const-type of een referentietype weer te geven. Deze modificaties kunnen worden gecombineerd.
In dit voorbeeld is s
een waardetype (het type wordt afgeleid als std::string
), dus elke iteratie van de for
lus kopieert een string van de vector naar s
.
std::vector<std::string> strings = { "stuff", "things", "misc" };
for(auto s : strings) {
std::cout << s << std::endl;
}
Als de body van de lus s
wijzigt (zoals door s.append(" and stuff")
aan te roepen), wordt alleen deze kopie gewijzigd, niet het oorspronkelijke lid van strings
.
Aan de andere kant, als s
wordt verklaard met auto&
zal het een referentietype zijn (afgeleid als std::string&
), dus bij elke iteratie van de lus krijgt het een verwijzing naar een string in de vector:
for(auto& s : strings) {
std::cout << s << std::endl;
}
In het lichaam van deze lus hebben wijzigingen in s
rechtstreeks invloed op het element van strings
waarnaar het verwijst.
Ten slotte, als s
verklaard const auto&
zal het een const referentie type, wat betekent dat bij elke iteratie van de lus zal een const verwijzing worden toegewezen aan een tekenreeks in de vector:
for(const auto& s : strings) {
std::cout << s << std::endl;
}
Binnen het lichaam van deze lus kunnen s
niet worden gewijzigd (er kunnen geen niet-const-methoden op worden gebruikt).
Bij het gebruik van auto
met range op basis for
loops, het is over het algemeen een goede gewoonte om te gebruiken const auto&
als de lus lichaam niet zal wijzigen van de structuur is een lus over, omdat dit onnodige kopieën vermijdt.
Volgend retourtype
auto
wordt gebruikt in de syntaxis voor het type met retourretour:
auto main() -> int {}
wat gelijk is aan
int main() {}
Meestal handig in combinatie met decltype
om parameters te gebruiken in plaats van std::declval<T>
:
template <typename T1, typename T2>
auto Add(const T1& lhs, const T2& rhs) -> decltype(lhs + rhs) { return lhs + rhs; }
Generieke lambda (C ++ 14)
C ++ 14 maakt het mogelijk om auto
in lambda-argument te gebruiken
auto print = [](const auto& arg) { std::cout << arg << std::endl; };
print(42);
print("hello world");
Die lambda komt grotendeels overeen met
struct lambda {
template <typename T>
auto operator ()(const T& arg) const {
std::cout << arg << std::endl;
}
};
en toen
lambda print;
print(42);
print("hello world");
auto- en proxy-objecten
Soms gedraagt auto
zich niet helemaal zoals een programmeur had verwacht. Het type leidt de uitdrukking af, zelfs wanneer typeaftrek niet het juiste is om te doen.
Als een voorbeeld, wanneer proxy-objecten in de code worden gebruikt:
std::vector<bool> flags{true, true, false};
auto flag = flags[0];
flags.push_back(true);
Hier flag
zou niet bool
, maar std::vector<bool>::reference
, aangezien voor bool
specialisatie van template vector
de operator []
geeft een proxyobject conversie met operator operator bool
gedefinieerd.
Wanneer flags.push_back(true)
de container wijzigt, kan deze pseudo-referentie gaan hangen, verwijzend naar een element dat niet meer bestaat.
Het maakt ook de volgende situatie mogelijk:
void foo(bool b);
std::vector<bool> getFlags();
auto flag = getFlags()[5];
foo(flag);
De vector
wordt onmiddellijk weggegooid, dus flag
is een pseudo-verwijzing naar een element dat is weggegooid. De oproep tot foo
roept ongedefinieerd gedrag op.
In dit geval kunt u een variabele declareren met auto
en deze initialiseren door te casten naar het type dat u wilt afleiden:
auto flag = static_cast<bool>(getFlags()[5]);
maar op dat moment is het eenvoudiger om auto
te vervangen door bool
.
Een ander geval waarbij proxy-objecten problemen kunnen veroorzaken, zijn expressiesjablonen . In dat geval zijn de sjablonen soms niet ontworpen om langer mee te gaan dan de huidige volledige expressie, en het gebruik van het proxy-object bij de volgende veroorzaakt ongedefinieerd gedrag.