Sök…
Anmärkningar
Nyckelordet auto
är ett typnamn som representerar en automatiskt härledd typ.
Det var redan ett reserverat nyckelord i C ++ 98, ärvt från C. I gamla versioner av C ++ kan det användas för att uttryckligen ange att en variabel har automatisk lagringsvaraktighet:
int main()
{
auto int i = 5; // removing auto has no effect
}
Den gamla meningen har nu tagits bort.
Grundläggande automatisk prov
Nyckelordet auto
ger automatisk avdrag för typen av en variabel.
Det är särskilt bekvämt när man handlar med långa typnamn:
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" );
med områdebaserade för slingor :
vector<int> v = {0, 1, 2, 3, 4, 5};
for(auto n: v)
std::cout << n << ' ';
med lambdas :
auto f = [](){ std::cout << "lambda\n"; };
f();
för att undvika upprepning av typen:
auto w = std::make_shared< Widget >();
för att undvika överraskande och onödiga kopior:
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!
Anledningen till kopian är att den returnerade typen faktiskt är std::pair<const int,float>
!
auto- och expressionsmallar
auto
kan också orsaka problem där expressionsmallar spelas in:
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.
Anledningen är att operator*
på valarray
ger dig ett proxyobjekt som hänvisar till valarray
som ett medel för lat utvärdering. Genom att använda auto
skapar du en dinglande referens. Istället för att mult
hade returnerat en std::valarray<int>
, skulle koden definitivt skriva ut 3.
auto, const och referenser
Det auto
nyckelordet i sig representerar en värdetyp, liknande int
eller char
. Det kan modifieras med const
nyckelordet och symbolen &
att representera en konsttyp respektive en referenstyp. Dessa modifierare kan kombineras.
I detta exempel är s
en värdetyp (dess typ kommer att sluts som std::string
), så varje iteration av for
loop kopierar en sträng från vektorn till s
.
std::vector<std::string> strings = { "stuff", "things", "misc" };
for(auto s : strings) {
std::cout << s << std::endl;
}
Om slingans kropp ändrar s
(t.ex. genom att ringa s.append(" and stuff")
) kommer endast den här kopian att ändras, inte den ursprungliga medlemmen i strings
.
Å andra sidan, om s
deklareras med auto&
det kommer att vara en referenstyp (utgår från att vara std::string&
), så på varje iteration av slingan kommer den att tilldelas en referens till en sträng i vektorn:
for(auto& s : strings) {
std::cout << s << std::endl;
}
I kroppen på denna slinga kommer ändringar av s
direkt att påverka elementet i strings
som det refererar till.
Slutligen, om s
förklaras const auto&
, kommer det att vara en const-referenstyp, vilket betyder att på varje iteration av slingan tilldelas en const-referens till en sträng i vektorn:
for(const auto& s : strings) {
std::cout << s << std::endl;
}
Inuti kroppen hos denna slinga, s
kan inte ändras (dvs. inga icke-const metoder kan anropas på det).
När du använder auto
med områdebaserade for
slingor är det i allmänhet god praxis att använda const auto&
om slingkroppen inte kommer att ändra strukturen som slingras, eftersom detta undviker onödiga kopior.
Släpningstyp
auto
används i syntaxen för efterföljande returtyp:
auto main() -> int {}
vilket motsvarar
int main() {}
Mycket användbart i kombination med decltype
att använda parametrar istället för std::declval<T>
:
template <typename T1, typename T2>
auto Add(const T1& lhs, const T2& rhs) -> decltype(lhs + rhs) { return lhs + rhs; }
Generisk lambda (C ++ 14)
C ++ 14 tillåter att använda auto
i lambda-argument
auto print = [](const auto& arg) { std::cout << arg << std::endl; };
print(42);
print("hello world");
Den lambda motsvarar mestadels
struct lambda {
template <typename T>
auto operator ()(const T& arg) const {
std::cout << arg << std::endl;
}
};
och då
lambda print;
print(42);
print("hello world");
auto- och proxyobjekt
Ibland kan auto
uppträda inte riktigt som förväntat av en programmerare. Den typen härleder uttrycket, även om typavdrag inte är rätt att göra.
Som exempel, när proxyobjekt används i koden:
std::vector<bool> flags{true, true, false};
auto flag = flags[0];
flags.push_back(true);
Här flag
skulle inte bool
, men std::vector<bool>::reference
, eftersom det för bool
specialisering av mall vector
den operator []
returnerar ett proxyobjekt med omvandlingsoperator operator bool
definierats.
När flags.push_back(true)
modifierar behållaren, kan denna pseudo-referens hamna dinglande, med hänvisning till ett element som inte längre finns.
Det gör också nästa situation möjlig:
void foo(bool b);
std::vector<bool> getFlags();
auto flag = getFlags()[5];
foo(flag);
vector
kasseras omedelbart, så flag
är en pseudo-referens till ett element som har kasserats. Uppmaningen till foo
åberopar odefinierat beteende.
I fall som detta kan du förklara en variabel med auto
och initiera den genom att kasta till den typ du vill dras från:
auto flag = static_cast<bool>(getFlags()[5]);
men vid den tidpunkten är det mer meningsfullt att helt enkelt byta ut auto
med bool
.
Ett annat fall där proxyobjekt kan orsaka problem är expressionsmallar . I så fall är mallarna ibland inte utformade för att sträcka sig utöver det nuvarande fulluttrycket för effektivitetsskull, och att använda proxyobjektet på nästa orsakar odefinierat beteende.