| ||||
| Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by Nemanja Trifunovic
There is one major problem with your reasoning, and it can be nicely summed up in this sentence: “Zilch. Not a single specific type appears anywhere in this code, and the lack of exact types makes it much more powerful and doesn't significantly harm its readability.” The code sample you gave is very non-typical. That is generic, template-based code usually written by library implementers. I write code like that in my open-source project, but never at work. Code I write at work is full of types and except in a few corner cases such as declarations of iterators explicit types do improve reliability and robustness of code. In short, I think I agree with your advice to use “auto” as much as possible, but only for template libraries. For all other code, “auto” should be used only in very few cases. Read More »Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by ned
Another downside of using auto is search-ability – it makes it very hard to search a code base to find all the places that an object is used. This can be an issue re-factoring an interface – they don’t always get written perfectly the 1st time. In a smaller project, a good IDE can help here, but when a code base spreads across 10′s or 100′s of projects its hard to beat find-in-files. Read More »Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by mttpd
A few extra thoughts and a question ;-) * “Compile-time duck typing” is generally referred to as “structural typing” (or “property-based typing”): * In the “What exact type is Container?” paragraph, perhaps it’s also worth mentioning on how it helps with satisfying the open/closed principle? * I would still advise against using auto for the loops’ counting/indexing variables: This could have been solved if we only had the suffix for std::size_t (ZU anyone? ;]), but as of today I don’t really see any alternative. * How does decltype(auto) fit into the big picture? :-) Read More »Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by avjewe
What if you want to use the default constructor? Comment on GotW #7a: Minimizing Compile-Time Dependencies, Part 1 by Gennaro
Hi Herb, a little note just in case there’s an unintended limitation: > You may not make any changes other than removing or rewriting #include directives Is the prohibition to forward declare intentional or was the wording just changed at the latest moment resulting too strict? Read More »Comment on GotW #7a: Minimizing Compile-Time Dependencies, Part 1 by helloworld922
2. With only modifying the #include statements the only obvious inclusion I can get rid of is iostream (alt. can get rid of ostream, but I like including ostream instead of iostream because it’s more specific, and this doesn’t rely on iostream including ostream internally). With forward declarations of class C and class E, c.h and e.h can be safely moved to x.cpp. Also, shouldn’t the Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by Jeffrey Bosboom
Two points about auto and expression templates: –If you use ‘auto a = x * 5someliteral’ where * returns an expression template, that template has a reference to a temporary object that will be destroyed at the end of the expression. So you can use auto to hold an expression template object, but you can’t safely do much with it (in particular, evaluate it). This feels like a trap, especially when combined with the next point. –If you change an operator that used to return a Foo to return an expression template convertible to a Foo, any code that used to use auto can break as above because it gets an expression template. Despite using auto, if you want the flexibility to introduce expression templates after-the-fact (say, after profiling shows you need them), you need to use the ‘auto a = Foo{expr}’ form everywhere. And because there’s no types, it’ll be hard to find the locations whose meaning changed, unless you have an IDE that can find all usages of e.g., operator*. You might not even know about breakage if it’s in some template (and similarly the template author has to decide between ‘auto a =x*y’ and ‘auto a = T{x*y}’). I recall there being some consideration of an ‘operator auto()’ to instruct the compiler what type to infer, and I’m not yet convinced it isn’t necessary. Read More »Comment on GotW #7a: Minimizing Compile-Time Dependencies, Part 1 by Alf P. Steinbach
In C++03 the <ostream> header could not be formally removed if one used e.g. std::endl, or the << operator. However, in practice one could use just <iostream> (as all examples in the C++03 standard erroneously did), and with C++11 that’s also formally supported. In the above code it’s not an issue, so <ostream> can just be removed. I would also replace full <iostream> with <iosfwd>. Class E is only used result and argument type, and so its header can be removed. The situation for class C is more complicated. The C++03 standard (I’m not 100% sure of C++11 and don’t have the time to check now, sorry) required the item type of a standard library container such as the std::list in this code, to be a complete type. However, this formal requirement was regularly sinned against with e.g. std::auto_ptr, by ensuring that the code conformed to the actual requirements of the particular C++ implementation. The most infamous case of this lets-be-practical-about-it approach was one of your own PIMPL GOTWs.. :-) But anyway, at least for the formal class C’s header is needed, since C is used as a container item type, so, only the header for E can be omitted. I would put a de facto standard #pragma once at the of that header. Read More »Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by zadecn
the codes and comments below have a little bug: auto b1 = my_vector_bool.back(); // to capture the proxy I think the right is : Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by noniussenior
“Quick quiz: How many specific types are mentioned in that function? Name as many as you can. Why is void not a specific type? Read More »Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by tr3w
Because of what Jeffrey Bosboom said, I think the my_vector_bool is a really bad example, since the following two code does something really different (and because the vector specialization already considered as a mistake). auto b1 = my_vector_bool.back(); // to capture the proxy b1 = true; // write into my_vector_bool auto i1 = my_vector_int.back(); // No proxy i1 = true; // NOT write into my_vector_intRead More » Comment on GotW #7a: Minimizing Compile-Time Dependencies, Part 1 by Victor
JQ #1: For a function or a class, what is the difference between a forward declaration and a definition? For function: Forward declaration specifies function’s return type and signature (parameters, cv-qualifiers if memeber, template parameters if any). It tells compiler about how to call this function, so parameters sizes should be known. Parameter size if known for any pointers, references or for complete types. For class: Class forward declaration provides incomplete type, including class name and template parameters (if any). Note, it doesn’t provide information about inheritance from base classes. Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by Petter
See e.g. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=505 for bugs/misunderstandings involving expression templates and auto. Read More »Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by Marcel Wid
I personally prefer to use auto to declare local variables. But unfortunately there are some problems with auto and uniform initialization. Perhaps it is a good idea to shed some more light on these pitfalls: auto x = {1}; The type of x will most likely not what you expect: Here x will have type std::initializer_list! For another example consider the following code: class X { public: X() : x_{0} {} private: int x_; }; double f() { return 4.2; } double d{f()}; We teach people to use uniform initialization (in this case direct-list-initialization) for class members like: double d(f()); But if we write auto d{4.2}; Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by gnzlbg
Do you have a reference to as_signed/as_unsigned ? Read More »Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by Petter
I must say that the following is very error-prone: auto b1 = my_vector_bool.back(); // ... b1 = true; // Oops! my_vector_bool changed! compared to bool b1 = my_vector_bool.back(); // ... b1 = true; // vector not changed. In almost all cases, auto will give you a copy. vector is error-prone combined with auto. Read More »Comment on GotW #7a: Minimizing Compile-Time Dependencies, Part 1 by Anubhav
In accordance with $3.2/4 – Comment on GotW #94 Solution: AAA Style (Almost Always Auto) by Norbert Riedlin
I really like one more in C++14 upcoming application of auto: class C { private: // real internal things: // C++11: class Internal; Internal doSomething(); // C++14: //auto doSomething(); }; In C++11 and before I have to name the return type of “doSomething()”. Although that can be done by forward declaring a local class, it feels even nicer not to have to mention that class in C++14. (At least that is how I understand N3638) Read More »Comment on GotW #7a: Minimizing Compile-Time Dependencies, Part 1 by Victor
Guru Question: In the following header file, what #include directives could be immediately removed without ill effect? You may not make any changes other than removing or rewriting #include directives. Note that the comments are important. About std headers: 1. Including automatically includes also , , , and . So, we don’t need both and , simple is enouth. However, there is references only for ostream classes in function declarations – so we don’t need complete ostream type here. (Remember about function declarations). Just use forward declaration from instead of and . 2. Including is needed – we use std::list as data member, so compiler needs complete type information here. About custom headers: 1. class X is derived from A and B. So, compiler heeds complete type information here, leave “a.h” and “b.h” as is. 2. Class C is a great candidate for further removal, but it’s used in function declaration as parameter with passing by-value. It’s sad, but compiler needs to know about what class C is right here, so complete type is needed, and we leave #include “c.h” in code. Additionally, it is strange that class C has virtual functions, but is passed by-value. Schedule a meet with this code author :) 3. Class D is a data member. So, we need to leave “d.h” 4. Class E is used as return type in function declarations only. It’s strange, but compiler doesn’t need complete type here. AFAIR, it was side effect of C calling convention on x86 compilers. Maybe I’m wrong about why it’s not not needed (please correct me?) but I think, we could change #include “e.h” to forward declaration on class E; So, there is result: // #include <iostream> - removed // #include <ostream> - changed to iosfwd #include <iosfwd> // placed instead of <ostream> #include <list> // None of A, B, C, D or E are templates. // Only A and C have virtual functions. #include "a.h" // class A #include "b.h" // class B #include "c.h" // class C - candidate for further removal by rewriting 'C f( int, C );' signature and adding forward declaration #include "d.h" // class D #include "e.h" // class E - candidate for removal by changing to forward declaration right here and right now. class X : public A, private B { public: X( const C& ); B f( int, char* ); C f( int, C ); C& g( B ); E h( E ); virtual std::ostream& print( std::ostream& ) const; private: std::list<C> clist; D d_; }; std::ostream& operator<<( std::ostream& os, const X& x ) { return x.print(os); } I think, another optimization will result in redesign, so we can’t omit more headers right now. Read More »Comment on GotW #7a: Minimizing Compile-Time Dependencies, Part 1 by Victor
Guru Question #2. Many programmers habitually #include many more headers than necessary. Unfortunately, doing so can seriously degrade build times, especially when a popular header file includes too many other headers. 1. Including automatically includes also , , , and . 2. Next step about io streams usage: there is references to streams only. Compiler initially knows reference size for any class, so forward declaration is enough here. Replace streams implementation header with forward declaration: . That’s all about std headers – unfortunately, we use std::list as member, so full std::list class definition is needed. Lease as is. Class X is derived from A and B, so we need to know what A and B is – leave “a.h” and “b.h”. Class C is candidate for further header removal, unfortunatelly, it’s passed into function declarations by value, so compiler need to know what is C, what constructors it have, etc. So, draw a sigh and leave “c.h”. Class D is a data membe – compiler needs D declaration. Leave “d.h” Class E is used in return type only – that’s good news. Return type might be incomplete type. This may be surprise, AFAIR, it’s good side effect of C calling conversions. I may be wrong in describing why it may be omitted (please correct me?) but I think we can remove “e.h”. So, we have: #include <iosfwd> #include <list> // None of A, B, C, D or E are templates. // Only A and C have virtual functions. #include "a.h" // class A #include "b.h" // class B #include "c.h" // class C - it's sad to see it here #include "d.h" // class D class X : public A, private B { public: X( const C& ); B f( int, char* ); C f( int, C ); C& g( B ); E h( E ); virtual std::ostream& print( std::ostream& ) const; private: std::list<C> clist; D d_; }; std::ostream& operator<<( std::ostream& os, const X& x ) { return x.print(os); } Read More » Comment on GotW #7a: Minimizing Compile-Time Dependencies, Part 1 by abrarov
// x.h: original header // #ifndef X_H #define X_H
Subscribe to:
Post Comments (Atom)
|
No comments:
Post a Comment