Schlüsselwort AUTO in c++0x++ , Diskussion, Erläuterung



  • Ich glaube er meint auto?



  • register war doch früher dafür damit man eine variable in die speicherregister des prozessors setzen konnte. aber wurde dann deprecated weil der compiler es besser wusste, genau wie inline.

    auto hat früher doch die gültigkeit angegeben, bzw wurde als default einstellung angesehen.

    ja er meinte auto denke ich



  • sorry habe wirklich auto gemeint keine ahnung wie ich da auf register gekommen bin,



  • gegen auto statt int spricht die Länge...

    Der eigentliche Grund für das "neue auto" ist eben, um lange Typen abzukürzen
    (speziell in Hinblick auf Templates)

    siehe z.B. eine Iteration über einen Vector in C++03 und C++11:

    vector<int> foo;
    
    // C++03
    for (vector<int>::iterator it = foo.begin(); it != foo.end(); ++it)
        // blub
    
    // C++11
    for (auto it = begin(foo); it != end(foo); ++it)
        // blub
    
    // oder noch kürzer (ebenfalls C++11)
    for (auto elem : foo)
        // blub
    

    Ebenso kann man Typen wie std::string::size_type damit abkürzen.

    Eine andere Anwendung ist das Speichern einer Lambda-Funktion in einer Variablen.
    da [](){} keinen in C++ ausdrückbaren Typ besitzt, hat man hier zwei Möglichkeiten:

    // auto
    auot fn = [](){};
    
    // std::function
    function<void ()> fn = [](){};
    

    Lambdas sind in C++11 implizit in std::function's konvertierbar.
    auto ist hier jedoch mal wieder kürzer und in wiefern die Konvertierung eines Lambda-Objekts in eine std::function auf Kosten der Größe oder der Performance geht,
    dürfte auch wieder vom Compiler und der entsprechenden Standardbibliothek abhängen.

    Also hier wäre auch wieder auto vorzuziehen.



  • Auch DRY (Dont Repeat Yourself) wird mit auto erreicht (nicht nur, aber auch).



  • Ich nutze mittlerweile auch ziemlich oft auto. Bei int/float etc. eher nicht, aber schon bei size_types etc. ist es praktisch.



  • auto war früher völlig undenkbar, weil man in Basic keine Typen angeben mußte, und Basic war böse.
    Man hat sich tatsächlich etwas darauf eingebildet, daß man immer Typen hinschreiben muß. Man war stolz darauf und hat gesagt, das würde zur Fehlervermeidung dienen. So, wie manche lokale Variablen alle am Funktionsanfang definieren, mit UML planen, Struktogramme oder ungarische Notation benutzen. Zwar völlig sinnfrei, wird aber gemacht und dann auch mit religiösem Eifer.

    Hin und wieder würde ich die Typen aber stehenlassen, besser als auto mit Kommentaren.



  • Ich benutze auto immer dann, wenn die Variable, die ich definiere, erstens dauerhaft mit dem Ausdurck zu tun hat, mit dem sie initialisiert wird und zweitens wenn aus dem Initializer klar ersichtlich ist, welcher Typ das "ungefähr" ist. "Ungefähr" heißt z.B., dass ein size() irgendeinen integralen Typ liefert und ich mehr nicht wissen muss. Ein value() ist dagegen nicht so klar - wenn ich annehme, dass ich es mit einem string zu tun habe, schreibe ich das hin, um gleich dort die Fehlermeldung zu kriegen, dass es in Wirklichkeit ein int ist. Sonst verschleppe ich den Typirrtum nur und wundere mich an anderer Stelle über seltsame Fehlermeldungen oder (noch schlimmer) unerwartetes Verhalten.

    Die Diskussion um Konvertierungskonstruktoren und Quarkmonkey-Scripts hab ich mal abgespalten. Hier bitte im Topic bleiben.



  • DrakoXP schrieb:

    Der eigentliche Grund für das "neue auto" ist eben, um lange Typen abzukürzen

    Also eigentlich gibt es zwei eigentliche Gründe.
    Wobei ich den anderen für den eigentlicheren halte: zu ermöglichen Variablen von "implementation-defined" Typen anzulegen:

    auto fun = [&x, y](Foo f) { f.Bar(x, y); };
    


  • Nicht zu vergessen auch trailing-return-types:

    template<class Lhs, class Rhs>
      auto adding_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}
    

  • Administrator

    Ich stand am Anfang auch kritisch gegenüber var in C#, was eigentlich das Äquivalent zu auto in C++11 ist. Heute brauche ich var in C# andauernd und bin immer enttäuscht, wenn eine Sprache sowas nicht anbietet.

    Customer customer = new Customer();
    // vs.
    var customer = new Customer();
    
    Customer* customer = new Customer();
    std::shared_ptr<Customer> customer = std::make_shared<Customer>();
    // vs.
    auto customer = new Customer();
    auto customer = std::make_shared<Customer>();
    
    Customer customer = new Customer();
    // vs.
    // ACH ... mist
    

    Ich verwende es zum Teil sogar schon für skalare Typen. Wenn man mal anfängt es zu verwenden, merkt man auch ganz schnell, dass der Typ sowas von überflüssig ist für das Verständnis. Man ist gar nicht daran interessiert. Zudem fördert es, dass man einen sinnvollen Variablennamen auswählt.

    Hier z.B. ein kürzlich von mir geschriebenes Program (war ein kleiner Test):

    #include <fstream>
    #include <iostream>
    
    #include <oppcl/oppcl.hpp>
    
    char const* const source =
      "__kernel void multiply(__global float* input, __global float* output)"
      "{"
      "  size_t idx = get_global_id(0);"
      "  output[idx] = input[idx] * input[idx];"
      "}";
    
    void print_log(char const* txt)
    {
      std::clog << txt << '\n';
    }
    
    int main()
    {
      print_log("Get platform...");
      auto platform_id = oppcl::get_all_platforms().at(0);
    
      print_log("Get device...");
      auto device_id = oppcl::get_all_devices(platform_id).at(0);
    
      float input_src[] = { 1, 2, 3, 4 };
      float output_dest[4] = { };
    
      print_log("Create queue and context...");
      auto queue = oppcl::create_command_queue(device_id);
      auto ctx = oppcl::get_context(queue);
    
      print_log("Create input buffer...");
      auto mem_input = oppcl::create_read_buffer<float>(ctx, 4);
    
      print_log("Create output buffer...");
      auto mem_output = oppcl::create_write_buffer<float>(ctx, 4);
    
      print_log("Write to input buffer...");
      oppcl::async_write(queue, mem_input, input_src, 4);
    
      print_log("Create program...");
      auto program = oppcl::create_program(ctx, source);
    
      print_log("Build program...");
      oppcl::build_program(program);
    
      print_log("Get kernel...");
      auto kernel = oppcl::create_kernel(program, "multiply");
    
      oppcl::wait_until_finished(queue);
    
      print_log("Set kernel arguments...");
      oppcl::set_kernel_arg(kernel, 0, mem_input);
      oppcl::set_kernel_arg(kernel, 1, mem_output);
    
      print_log("Run kernel...");
      oppcl::run_kernel(kernel, queue, 4);
    
      oppcl::wait_until_finished(queue);
    
      print_log("Read output buffer...");
      oppcl::async_read(queue, mem_output, output_dest, 4);
    
      oppcl::wait_until_finished(queue);
    
      print_log("Print results...");
      for(int i = 0; i < 4; ++i)
      {
        std::cout << output[i] << '\n';
      }
    
      std::cin.get();
    
      return 0;
    }
    

    oppcl::create_command_queue gibt z.B. oppcl::shared_handle<cl_command_queue> zurück. Aber ist das wirklich wichtig für das Verständnis des Programmes? Meiner Meinung nach nicht.

    Grüssli



  • Dravere schrieb:

    Ich verwende es zum Teil sogar schon für skalare Typen.

    Das wird halt dann interessant, wenn man keinen expliziten Typ als Rückgabewert hat, sondern z.B. nur eine Zahl. Dann nimmt man doch mal lieber unsigned oder float. Ansonsten stimme ich dir zu, bei (fast*) allem wo man eine Funktion nimmt die irgendwas zurückgibt, ist auto super.

    * Mit Referenzen sollte man aufpassen, wenn man nur eine Referenz will, sonst wird das nämlich kopiert.



  • cooky451 schrieb:

    * Mit Referenzen sollte man aufpassen, wenn man nur eine Referenz will, sonst wird das nämlich kopiert.

    Wo ist das Problem? Wenn ich explizit eine Referenz haben möchte schreibe ich halt auto& 🙂 Darauf müsste ich beim Ausschreiben des Typen genauso achten, ist also unabhängig von der auto-Geschichte.

    cooky451 schrieb:

    Dravere schrieb:

    Ich verwende es zum Teil sogar schon für skalare Typen.

    Das wird halt dann interessant, wenn man keinen expliziten Typ als Rückgabewert hat, sondern z.B. nur eine Zahl.

    Beispiel? Man hat doch immer einen expliziten Typen als Rückgabetyp. Und grade wenn das einer der implementatoin-defined Typen ist, ist auto erst recht gut - man hat dann genau den Typen den man braucht. groß/genau genug aber nicht zu groß/genau.



  • pumuckl schrieb:

    cooky451 schrieb:

    * Mit Referenzen sollte man aufpassen, wenn man nur eine Referenz will, sonst wird das nämlich kopiert.

    Wo ist das Problem? Wenn ich explizit eine Referenz haben möchte schreibe ich halt auto& 🙂

    Ou, daran hatte ich gar nicht gedacht, das ist natürlich cool. 👍

    pumuckl schrieb:

    cooky451 schrieb:

    Das wird halt dann interessant, wenn man keinen expliziten Typ als Rückgabewert hat, sondern z.B. nur eine Zahl.

    Beispiel? Man hat doch immer einen expliziten Typen als Rückgabetyp. Und grade wenn das einer der implementatoin-defined Typen ist, ist auto erst recht gut - man hat dann genau den Typen den man braucht. groß/genau genug aber nicht zu groß/genau.

    Beispiel?

    for (auto i = 0; ..)
    


  • cooky451 schrieb:

    pumuckl schrieb:

    cooky451 schrieb:

    Das wird halt dann interessant, wenn man keinen expliziten Typ als Rückgabewert hat, sondern z.B. nur eine Zahl.

    Beispiel? Man hat doch immer einen expliziten Typen als Rückgabetyp. [...]

    Beispiel?

    for (auto i = 0; ..)
    

    0 ist nicht nur einfach eine Zahl sondern hat einen Typ: int. auto verhält sich hier genauso wie Template-Argument-Deduction bei Funktionstemplates.

    0   --> int
    0L  --> long int
    0u  --> unsigned int
    0uL --> unsigned long int
    0.0 --> double
    0.f --> float
    

    Lässt man das u bei Ganzzahlen weg, schreibt dafür die Zahl aber in Hex hin, erhält man auch ggf eine Zahl eines vorzeichenlosen Typs.

    Eine Sache kann auto aber mehr:

    auto foo = {1,2,3}; // decltype(foo) --> initializer_list<int>
    

    Das klappt nur bei auto und nicht bei Funktionstemplates.



  • krümelkacker schrieb:

    0 ist nicht nur einfach eine Zahl sondern hat einen Typ: int. auto verhält sich hier genauso wie Template-Argument-Deduction bei Funktionstemplates.

    Ja, schon. Aber dann schreibst du wirklich statt "unsigned i = 0" "auto i = 0u"?



  • Nö, das habe ich nicht behauptet. Aber ich verwende schonmal entsprechende Suffixe. Blödes aber einfaches Beispiel:

    bool is_odd(int z) { return z & 1u; }
    

    Damit erzwinge ich hier eine arithmetische Konvertierung int -> unsigned von z bevor & angewendet wird. Diese Konvertierung garantiert mir das Bitmuster eines 2er-Komplements; denn sonst könnte das bei negativen Zahlen und einer anderen Darstellung (1er Komplement) nach hinten losgehen. Die Bitoperationen bei negativen Werten sind einfach nicht klar festgelegt. Bei vorzeichenlosen Zahlen schon.



  • krümelkacker schrieb:

    Nö, das habe ich nicht behauptet. Aber ich verwende schonmal entsprechende Suffixe. Blödes aber einfaches Beispiel:

    bool is_odd(int z) { return z & 1u; }
    

    Damit erzwinge ich hier eine arithmetische Konvertierung int -> unsigned von z bevor & angewendet wird. Diese Konvertierung garantiert mir das Bitmuster eines 2er-Komplements; denn sonst könnte das bei negativen Zahlen und einer anderen Darstellung (1er Komplement) nach hinten losgehen. Die Bitoperationen bei negativen Werten sind einfach nicht klar festgelegt. Bei vorzeichenlosen Zahlen schon.

    Aber täte

    bool is_odd(int z) { return z%2==0; }
    

    nicht das besser tun? Weil man gar nicht um Bitgefrickel nachdenken muß, sondern eher in der Mathematik bleibt.



  • volkard schrieb:

    nicht das besser tun? Weil man gar nicht um Bitgefrickel nachdenken muß, sondern eher in der Mathematik bleibt.

    Wurde das nicht nur mit unsigned optimiert? 🤡



  • cooky451 schrieb:

    Wurde das nicht nur mit unsigned optimiert? 🤡

    Verwechselst Du das mit %1024?


Anmelden zum Antworten