2015-06-05 13 views
6

Zauważyłem, że jeden może używać charakterystyki kompilacji zmiennych, które nie zostały przechwycone w lambda, np. zadzwoń sizeof, decltype funkcje, na przykład:Jaki jest sens korzystania z właściwości czasu kompilacji niezapisanych zmiennych lokalnych w lambda?

#include <iostream> 
void f() 
{ 
} 

int main() 
{ 
    int y = 13; 
    auto x = []{ return sizeof (decltype (y));}; 
    std::cout << x() << '\n'; 
} 

zarówno jako g++ i clang++ skompilować program bez błędów Chyba jest to dozwolone przez standard.

Wydaje mi się to trochę mylące, mimo że nie mogę wymyślić żadnego konkretnego złośliwego przypadku, w którym doprowadzi to do błędu. Ale zastanawiam się, jakie są rzeczywiste przypadki użycia tej funkcji?

+3

Mówiąc bardziej ogólnie, wystarczy przechwycić zmienne lokalne, jeśli są one * odr-używane *. Na przykład możesz użyć wartości lokalnych zmiennych 'constexpr', takich jak' constexpr int x = 42; '. – dyp

+0

Wydaje mi się całkiem rozsądny, nie chciałbym mieć zmiennych, do których nie mam dostępu w zakresie przechwytywania. –

+0

Jakie są faktyczne przypadki użycia cegły budowlanej? –

Odpowiedz

5

Prostym przykładem, w którym możesz go użyć, jest lambda, w której chcesz wykonać obliczenia w tym samym typie co y, ponieważ przypisujesz wynik do y.

Pomyśl o tym na odwrót: jaka jest korzyść z przechwytywania y w [=]{ return x + sizeof (y);}? Nie ma absolutnie żadnego sensu, ponieważ y nie jest faktycznie używany wewnątrz lambda. Przechwytywanie y spowodowałoby dodanie zupełnie bezsensownego narzutu. Co więcej, może nawet skomplikować wewnętrzne działanie kompilatorów, ponieważ nie będą w stanie po prostu zoptymalizować y z dala, sizeof(y) nie jest już semantycznie równoważny z sizeof(int).

+0

OK, o ile rozumiem, jeśli przechwycisz zmienną przez odniesienie i zniknie ona poza zasięgiem, podczas gdy nie dodasz w tym przypadku żadnego narzutu, możesz ryzykować użycie tej zmiennej wewnątrz lambda, kiedy jest już wyszedł poza zakres. Więc to chyba właściwe rozumowanie. – Predelnik

0

Czasami w zakresie zagnieżdżonym (lambda) potrzebujesz informacji o typie bez wartości. Zawsze możesz nazwać typ (lub parametr szablonu) bezpośrednio, jeśli masz do niego dostęp, ale zawsze jest taka miejska legenda dobra rada, mówiąc, że jeśli pewnego dnia zmienisz typ zmiennej, nie będziesz musiał jej zmieniać we wszystkich innych wyrażenia, w których je powtórzyłeś.

Na przykład:

#include <iostream> 
#include <tuple> 
#include <utility> 

class storage 
{ 
public: 
    template<typename T> 
    auto make_getter(T value) 
    { 
    std::get<decltype(value)>(storage_) = value; 

    auto getter = [this] 
     { 
     return std::get<decltype(value)>(storage_); 
     }; 

    return getter; 
    } 

private: 
    std::tuple<int, char, double> storage_; 
}; 

int  main(void) 
{ 
    storage  s; 

    auto getter = s.make_getter(42); 

    std::cout << getter() << std::endl; 
} 

Tutaj można zawsze wykorzystać std::get<T> zamiast std::get<decltype(value)> ale jeśli kiedyś make_getter nie jest szablonem więcej i staje się to normalna funkcja przeciążenia dla każdego rodzaju krotki niż typ wartości zmieniłoby się na int na przykład, zaletą jest to, że decltype(value) zawsze będzie działać pod warunkiem, że nie zmienisz nazwy zmiennej.

W każdym razie uważam, że poziom użyteczności tej funkcji może być bardziej semantyczny niż techniczny. Takie zachowanie jest prawdopodobnie odziedziczone po starej szkole kanonicznej

char *buffer = malloc(42 * sizeof(*buffer)); 

stosowanego w języku zamiast

char *buffer = malloc(42 *sizeof(char)); 

z tych samych powodów C.

Również jeśli nazwa typu jest czymś nie do zniesienia, że ​​z jakiegoś powodu nie chcesz używać aliasu, przejdziesz do stylu decltype, co niekoniecznie oznacza, że ​​chcesz uzyskać powiązaną wartość.