Szukaj…


Uwagi

Słowo kluczowe auto to nazwa typu reprezentująca typ automatycznie wywnioskowany.

To było już zastrzeżone słowo kluczowe w C ++ 98, odziedziczone po C. W starych wersjach C ++ można go było użyć do jawnego stwierdzenia, że zmienna ma automatyczny czas przechowywania:

int main()
{
  auto int i = 5; // removing auto has no effect
}

To stare znaczenie zostało teraz usunięte.

Podstawowa próbka automatyczna

auto kluczowe auto zapewnia automatyczne odjęcie typu zmiennej.

Jest to szczególnie wygodne w przypadku długich nazw typów:

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

z pętlami zależnymi od zasięgu :

vector<int> v = {0, 1, 2, 3, 4, 5};
for(auto n: v)
    std::cout << n << ' ';

z lambdami :

auto f = [](){ std::cout << "lambda\n"; };
f();        

aby uniknąć powtórzenia tego typu:

auto w = std::make_shared< Widget >();

aby uniknąć zaskakujących i niepotrzebnych kopii:

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!

Powodem kopiowania jest to, że zwracanym typem jest w rzeczywistości std::pair<const int,float> !

szablony automatyczne i wyrażenia

auto może również powodować problemy w przypadku użycia szablonów wyrażeń:

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.

Powodem jest to, że operator* na valarray daje ci obiekt proxy, który odwołuje się do valarray jako sposobu leniwej oceny. Korzystając z auto , tworzysz wiszące odniesienie. Zamiast mult zwrócił std::valarray<int> , wtedy kod na pewno wypisze 3.

auto, const i referencje

Samo słowo kluczowe auto reprezentuje typ wartości podobny do int lub char . Można go modyfikować za pomocą słowa kluczowego const i symbolu & aby reprezentować odpowiednio typ const lub typ odwołania. Te modyfikatory można łączyć.

W tym przykładzie s jest typem wartości (jego typ będzie wywnioskowany jako std::string ), więc każda iteracja pętli for kopiuje ciąg z wektora do s .

std::vector<std::string> strings = { "stuff", "things", "misc" };
for(auto s : strings) {
    std::cout << s << std::endl;
}

Jeśli s.append(" and stuff") pętli modyfikuje s (na przykład przez wywołanie s.append(" and stuff") ), tylko ta kopia zostanie zmodyfikowana, a nie oryginalny element strings .

Z drugiej strony, jeśli s jest zadeklarowane za pomocą auto& będzie to typ odwołania (domyślnie jest to std::string& ), więc przy każdej iteracji pętli zostanie przypisane odwołanie do ciągu w wektorze:

for(auto& s : strings) {
    std::cout << s << std::endl;
}

W treści tej pętli modyfikacje s będą miały bezpośredni wpływ na element strings których się odwołuje.

Na koniec, jeśli s zostanie zadeklarowane jako const auto& , będzie to stały typ odwołania, co oznacza, że przy każdej iteracji pętli zostanie mu przypisane stałe odniesienie do łańcucha w wektorze:

for(const auto& s : strings) {
    std::cout << s << std::endl;
}

W ramach tego ciała pętli, s nie mogą być zmienione (np żadnych metod const mogą być wywoływane na nim).

Kiedy używasz auto z pętlą opartą for , ogólnie dobrą praktyką jest używanie const auto& jeśli korpus pętli nie zmodyfikuje zapętlonej struktury, ponieważ pozwala to uniknąć niepotrzebnych kopii.

Końcowy typ zwrotu

auto jest używane w składni dla końcowego typu powrotu:

auto main() -> int {}

co jest równoważne z

int main() {}

Najbardziej użyteczny w połączeniu z decltype do użycia parametrów zamiast std::declval<T> :

template <typename T1, typename T2>
auto Add(const T1& lhs, const T2& rhs) -> decltype(lhs + rhs) { return lhs + rhs; }

Ogólna lambda (C ++ 14)

C ++ 14

C ++ 14 pozwala na użycie auto w argumencie lambda

auto print = [](const auto& arg) { std::cout << arg << std::endl; };

print(42);
print("hello world");

Ta lambda jest w większości odpowiednikiem

struct lambda {
    template <typename T>
    auto operator ()(const T& arg) const {
        std::cout << arg << std::endl;
    }
};

i wtedy

lambda print;

print(42);
print("hello world");

obiekty auto i proxy

Czasami auto może zachowywać się niezupełnie tak, jak oczekiwał tego programista. Typ dedukuje wyrażenie, nawet jeśli dedukcja typu nie jest właściwa.

Na przykład, gdy w kodzie są używane obiekty proxy:

std::vector<bool> flags{true, true, false};
auto flag = flags[0];
flags.push_back(true);

flag nie byłaby bool , ale std::vector<bool>::reference , ponieważ dla specjalizacji bool vector szablonowy operator [] zwraca obiekt proxy z zdefiniowanym operatorem konwersji operator bool .

Gdy flags.push_back(true) modyfikuje kontener, to pseudo-odwołanie może w końcu zwisać, odnosząc się do elementu, który już nie istnieje.

Umożliwia także następną sytuację:

void foo(bool b);

std::vector<bool> getFlags();

auto flag = getFlags()[5];
foo(flag);

vector jest natychmiast odrzucany, więc flag jest pseudo-odniesieniem do elementu, który został odrzucony. Wezwanie do foo wywołuje niezdefiniowane zachowanie.

W takich przypadkach możesz zadeklarować zmienną za pomocą auto i zainicjować ją, rzutując na typ, który chcesz wywnioskować:

auto flag = static_cast<bool>(getFlags()[5]);

ale w tym momencie po prostu zastąpienie auto bool ma większy sens.

Innym przypadkiem, w którym obiekty proxy mogą powodować problemy, są szablony wyrażeń . W takim przypadku szablony czasami nie są zaprojektowane tak, aby przetrwały poza bieżącym pełnym wyrażeniem ze względu na wydajność, a użycie obiektu proxy w następnym powoduje niezdefiniowane zachowanie.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow