2015-06-05 9 views
6

Ich bemerkte, dass man kompilieren-Zeit-Eigenschaften von Variablen, die nicht in Lambda, z. rufen sizeof, decltype Funktionen, zum Beispiel:Welchen Sinn hat es, Kompilierzeitmerkmale von nicht erfassten lokalen Variablen in einem Lambda zu verwenden?

#include <iostream> 
void f() 
{ 
} 

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

Da beide g++ und clang++ kompilieren dieses Programm ohne Fehler Ich denke, es von der Norm erlaubt.

Es scheint ein bisschen irreführend für mich, obwohl ich nicht an einen bestimmten bösartigen Fall denken kann, wo es zu einem Fehler führen wird. Aber ich frage mich, was sind die tatsächlichen Anwendungsfälle dieser Funktion?

+3

Allgemeiner müssen Sie nur lokale Variablen erfassen, wenn sie * odr-used * sind. Zum Beispiel können Sie den Wert von lokalen 'constexpr'-Variablen wie 'constexpr int x = 42;' verwenden. – dyp

+0

Scheint ziemlich vernünftig für mich, ich hätte nicht gerne Variablen, die ich nicht im Erfassungsbereich zugreifen. –

+0

Was sind die tatsächlichen Anwendungsfälle eines Bausteins? –

Antwort

5

Ein einfaches Beispiel, in dem Sie dies verwenden könnten, ist, wenn Sie ein Lambda haben, wo Sie Berechnungen vom selben Typ wie y durchführen möchten, weil Sie das Ergebnis y zuweisen werden.

Denken Sie auch anders herum: Was ist der Vorteil der Erfassung y in [=]{ return x + sizeof (y);}? Das hat absolut keinen Sinn, da y nicht wirklich innerhalb des Lambda verwendet wird. Capturing y würde nur völlig sinnlosen Overhead hinzufügen. Nicht nur das, es könnte sogar das Innenleben von Compilern verkomplizieren, da sie y nicht mehr einfach optimieren könnten, sizeof(y) ist nicht mehr semantisch äquivalent zu sizeof(int).

+0

OK, so weit ich verstehe, wenn Sie Variable durch Verweis erfassen und es später außerhalb des Geltungsbereichs sein wird, während Sie in diesem Fall keinen Aufwand hinzufügen würden, könnten Sie das Risiko eingehen, diese Variable innerhalb von Lambda zu verwenden, wenn dies der Fall ist schon außer Reichweite geraten. Das ist wahrscheinlich die richtige Überlegung. – Predelnik

0

Manchmal benötigen Sie in einem verschachtelten Bereich (Lambda) Typinformationen ohne den Wert. Sie können den Typ (oder den Vorlagenparameter) immer direkt benennen, wenn Sie Zugriff darauf haben, aber es gibt immer die städtische Legende, die besagt, dass Sie den Typ der Variablen nicht ändern müssen, wenn Sie sie eines Tages ändern möchten Ausdrücke, wo Sie es wiederholt haben.

Zum Beispiel:

#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; 
} 

Hier können Sie immer std::get<T> anstelle von std::get<decltype(value)> aber wenn ein Tag make_getter keine Vorlage mehr ist, und es wird eine normale Funktion für jeden Typen des Tupels als die Art von Wert überlastete würde zum Beispiel zu int ändern, der Vorteil hier ist, dass decltype(value) immer funktioniert, vorausgesetzt, Sie benennen die Variable nicht um.

Wie auch immer ich denke, der Nutzen dieser Funktion könnte semantischer als technischer sein. Dieses Verhalten wird wahrscheinlich von der alten Schule kanonischen

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

verwendete in der C Sprache statt

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

aus den gleichen Gründen.

Auch wenn der Typ Name etwas unerträglich ist, dass Sie aus irgendeinem Grund nicht Alias ​​werden möchten, gehen Sie die decltype Weg, die nicht unbedingt bedeutet, dass Sie den zugehörigen Wert möchten.

Verwandte Themen