8 ms·
I really like the direction C++ is moving in. I just really don't like how incredibly VERBOSE it is (though `auto` helps).
by spott 12y ago
I really like the direction C++ is moving in. I just really don't like how incredibly VERBOSE it is (though `auto` helps).
- astrodust 12y agoIn languages like C++ "verbose" means "specific and obvious" which is something you want to have when writing certain kinds of code. It's annoying, it's a drag on productivity, but in the long run it's arguably necessary. Some other languages which reduce verbosity by making more things implicit make it harder to understand what's actually going on behind the scenes. You lose a lot of information.
- SamReidHughes 12y ago> In languages like C++ "verbose" means "specific and obvious" Whoa, no it doesn't. C++ is far more verbose than necessary for things like creating algebraic datatypes or really creating any types.
- jjoonathan 12y agoOr looping through for loops. I remember the jaw-hitting-floor moment when my C++ book tried to pass off std::vector<int>::iterator it; for (it=arr.begin(); it!=arr.end(); it++) { ... } as an improvement over for (int i=0; i<len; i++) { ... } Not entirely unrelated: I'm currently busy verbosifying a large chunk of C++11 code into C++90 code because Reasons (or so the maintainers assure me).
- ThatOtherPerson 12y agoIt's an improvement in the sense that it's more general; you can loop through any container that implements that protocol, which most (if not all) of the STL containers do. For example, template <class T> void foo(T arr) { for (auto it = arr.begin(); it != arr.end(); ++it) { std::cout << *it << std::endl; } } You could pass a `std::map<int>` to `foo`, or a `std::list<string>`, or a `std::vector<char>`. You could even create your own classes and give them to `foo`, as long as they implement the `begin`/`end` protocol. It's certainly more verbose than necessary, but it can be convenient.
- jjoonathan 12y agoOh yes, and I'm hella thankful for those containers. I just wish "auto" had come along a little sooner :)
- nly 12y agoYou want to use 'T const&' as your argument type, not 'T', otherwise foo() will take a copy of anything you pass.
- zak_mc_kracken 12y agoI've never understood the STL's infatuation with iterators. Why do I even need to know about them when all I want is walk through the collection? Ideally, it should be something like: for (auto it : arr) { // do something with *it }
- jonstewart 12y agoIt basically is this easy these days, thankfully. It is nice to have iterators, though, because it allows you some flexibility for swapping out the underlying data structures on existing algorithmic code. This has proved quite useful when I've written graph code.
- jeorgun 12y agoYou can do that. That's part of C++11 and is supported by basically every major compiler. The advantage of iterators is that they're incredibly flexible. You can use them for sub-ranges, reversed ranges, non-ranges like input and output iterators, etc, etc. This is vastly more powerful than something like, say, Objective C's NSFastEnumeration, which just allows for the trivial case handled by the range-based for-loop.
- mpyne 12y ago> Why do I even need to know about them when all I want is walk through the collection? Because there are algorithms where you may not want to walk through the whole collection. STL wasn't meant to be a container library alone, it also includes many generic algorithms that work using iterators to delimit the range of data to be operated upon.
- azoth_ 12y agoIn modern C++ these loops could alternatively be written as: std::vector<int> myVec; for( auto it = myVec.begin(); it != myVec.end(); ++it ); or if you prefer the external begin/end syntax: for( auto it = std::begin(myVec); it != std::end(myVec); ++it ); or the most succinct (replace && by & if writing): for( auto && item : myVec );
- jjoonathan 12y agoYes, and it's probably good to note that the issue has been fixed so as to not unduly scare away any newcomers to C++. Thanks for doing it for me :) Still, at the time the for(auto& item : vec) syntax was merely a talking point being tossed around C++0x meetings. And to this day the project I'm working on still hasn't officially dropped support for C++90 so I'm stuck with the old syntax anyway :/
- pbsd 12y ago> or the most succinct (replace && by & if writing): That is strictly not necessary. `auto&&` is what is now being called a universal reference: `item` resolves to `const int&` if `myVec` is const, and to `int&` if `myVec` is mutable. `auto&&` does the right thing in the majority of cases. In fact, the following extension [1] has been proposed for C++17 (and already implemented in recent clang builds): for( item : myVec ) { /* do stuff */ } which is meant to be a short-hand for for( auto&& item : myVec ) { /* do stuff */ } [1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3994.htm http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n399...
- StephanTLavavej 12y agoBoth auto& and auto&& produce const T& for a const vector<T>, and T& for a modifiable vector<T>. The difference between them is subtle: auto&& handles Humanity's Eternal Nemesis, vector<bool>.
- pbsd 12y agoOops, you're right of course; I got reference collapsing rules confused with the regular binding rules. Proxy objects will only bind to auto const& or auto&&, though, since they're generally returned by value. I was surprised to see that this does not come up in std::bitset nor std::valarray, but only std::vector<bool>.
- 72deluxe 12y agoI remember reading Stroustrup's book and not having a clue what an "iterator" was. Even looking in dictionaries didn't help me - I was just after the very definition of the word! Times have changed now of course.
- kazinator 12y agoIndeed! Just to write a class in C++ with a constructor and destructor which are not inlined, you have to repeat the class name about seven times: three times in the class declaration. Then four more times in the definitions of those two functions: class verbose { public: verbose(); ~verbose(); }; verbose::verbose() { } verbose::~verbose() { }
- ajuc 12y agoA lot of C++ verbosity comes simply from bad defaults. Const and virtual should be the default for example, not the other way around. Would stop the virtual destructor ommision problem, and if you need efficiency you could make destructors non-virtual. Also in the "hiding stuff behind the scenes" department C++ is quite bad. SomeClass someMethod(SomeClass a, SomeClass b) { ... } is doing a lot behind the scenes. Code below is better but it's longer and less convenient to write. const SomeClass& someMethod(const SomeClass& a, const SomeClass& b) { ... } And actually C++ code isn't easy to reason about wihtout reading the whole program. It isn't even easy to parse. What's going to happen when you run this? y = f(x); It may be that f is function, or a type. f may return the correct type to assign to y, or it may return something else and automatically run some conversion. There may be copy constructor and some destructors involved if it's returning object and not reference. It may run overloaded operator= and do anything at all, for example add f(x) to y. Hell, f can also be a class with operator() overloaded, and you would need to track its state to see what will happen. And we haven't even touched the subject of #define. It's much easier to reason about Java code for example.
- wfunction 12y agoUh, no, "virtual" should absolutely NOT be the default. I don't know where you got that idea from (Java?) but it's absolutely horrible.
- ajuc 12y agoWhy would it be bad? You could always add non-virtual (inline maybe? it's already useless keyword) if you need the performance, and most of the code doesn't. And it's a source of errors.
- ixtli 12y agoBecause if the compiler can not figure out how to de-virtualize your usage of a structure then you're going to necessitate that a vtable be created at compile time and used at runtime. The reason the previous commenter asked if you're from the java world is because in many highly important areas, the effect on memory and speed this would cause would be unacceptable. These areas _tend_ to be left to people who understand languages like c, though, so a lot of newer languages make decisions ignoring these good use cases.
- RogerL 12y agoStroustrup doesn't think so. I don't have a link - it was in some recent conference. He stated that every time he added a new feature everyone would be up in arms and demand a really verbose implementation, which he added reluctantly. Now that everyone is used to the features they complain about how ridiculously verbose it all is. There isn't any reason for the craziness of template<typename T> void foo(T a) vs void foo(auto a) Certainly things can become too implicit, but C++'s heritage of verboseness is due to the conservatism of the standards committee, not because anything less would be confusing.
- syncsynchalt 12y agoThe reason for your particular example is path dependency — templates came long before (the redefinition of) auto.
- dllthomas 12y agoBut the expanded form does provide you with more flexibility. Even if the latter is a synonym for the former, there is a difference between: template<typename T> void foo(T a, T b) vs void foo(auto a, auto b)
- kazinator 12y agoIt seems like in the second `foo`, the types of `a` and `b` are not hinged together. So to do it with the template syntax you actually need: template<typename T, typename U> void foo(T a, U b) :)
- dllthomas 12y agoI intended the two to share a type. My point was that you cannot express that constraint with only auto.
- deleted 12y ago[deleted]
- spott 12y agoI was specifically referring to boilerplate and type level programming: Algebraic Data Types's, and type generation in general. C++'s type system is actually pretty strong, but the amount of boilerplate that is needed to create a new type means that it is very difficult to program in a type safe way... without being incredibly verbose. Creating a type safe "Length" type is too much of a pain in the ass for anyone to actually do it. Which is what I mean by "too verbose". It is so verbose that instead of securing the "specific and obvious" in strong compiler guarantees, we fake it with implicit conversions and typedefs.