Schlüsselwort AUTO in c++0x++ , Diskussion, Erläuterung
-
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) // blubEbenso 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;}
-
Ich stand am Anfang auch kritisch gegenüber
varin C#, was eigentlich das Äquivalent zuautoin C++11 ist. Heute brauche ichvarin 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 ... mistIch 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_queuegibt 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 --> floatLä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?
-
volkard schrieb:
Aber täte
bool is_odd(int z) { return z%2==0; }nicht das besser tun?
Es war leider das erste Beispiel, was mir einfiel. Ich würde wahrscheinlich auch einfach z%2**!=**0 der Lesbarkeit wegen schreiben und auf schlaue Compiler vertrauen, die daraus wieder eine Bitfrickelei machen.

-
volkard schrieb:
cooky451 schrieb:
Wurde das nicht nur mit unsigned optimiert?

Verwechselst Du das mit %1024?
Ne, ich beziehe mich da auf http://www.c-plusplus.net/forum/294476