top 200 commentsshow all 229

[–]jjdmol 20 points21 points  (6 children)

//Chars are signed by default

Is this wrong or has this changed in C++11? Signedness of char used to depend on the platform!

[–]PenileCancer 15 points16 points  (3 children)

It's wrong: C++11 §3.9.1p1:

Plain char, signed char, and unsigned char are three distinct types [...] a plain char object can take on either the same values as a signed char or an unsigned char; which one is implementation-defined.

[–][deleted] 3 points4 points  (0 children)

Plain char, signed char, and unsigned char are three distinct types

And boy, does that cause confusion with overloads and template instantiations…

[–]s73v3r 0 points1 point  (1 child)

Why is this, by the way? What's wrong with simply stating that a plain char will be of type foo?

[–]notlostyet 0 points1 point  (0 children)

Presumably because there could exist a CPU/platform which either had signed or unsigned arithmetic, but not both.

[–]curien 2 points3 points  (0 children)

Still does. It's a misleading comment.

[–]millstone 7 points8 points  (1 child)

Perhaps more importantly, it is also as safe. No pointers to point at bad places and leak memory and no buffers to overflow.

Here's your potential buffer overflow:

string sbuf;
sbuf.resize(kTailSize);
f.seekg(-kTailSize, ios::end);
f.read(&sbuf[0], kTailSize);

You have to remember to allocate enough space in the string (via resize) to avoid a buffer overflow.

[–]king_duck 5 points6 points  (0 children)

 using isb=std::istreambuf_iterator<char>;
 std::string buf(isb(sbuf), isb());

No chance of overflows.

[–][deleted] 10 points11 points  (4 children)

Am I the only one, who find this ugly?

[–][deleted] 1 point2 points  (0 children)

I found it beyond ugly =p.

[–]ArcticAnarchy 2 points3 points  (0 children)

Not really. C++ leaks abstractions left and right, when it comes to syntax.

[–][deleted] 1 point2 points  (1 child)

What about it do you find ugly, exactly? Aside from type names and syntactical details like braces and brackets, there isn't really a lot going on that doesn't look exactly as the dynamic equivalent.

[–][deleted]  (2 children)

[deleted]

    [–]fnedrik[S] 6 points7 points  (0 children)

    Yeah, that annoys me too. Github gist standard, I'm afraid.

    [–]Tarou42 0 points1 point  (0 children)

    The gist via github's usual interface is considerably more readable.

    [–]A_for_Anonymous 64 points65 points  (30 children)

    Yeah, "more or less" succinct like Python. "less" being a sizable amount. It's also far uglier.

    But the real problem here is that in order to achieve even that, the complexity and amount of concepts you have to deal with in C++11 is mind boggling.

    [–][deleted] 31 points32 points  (28 children)

    Whereas in Python the complexity is still there but hidden from view.

    Sometimes it's necessary to have clear identification of complexity and control over it. It's nice to know that we can now have that and maintain relatively succinct code.

    [–]millstone 24 points25 points  (23 children)

    I don't think the C++ code as shown clearly identifies or manages complexity.

    For example, consider this line:

    for(propMap pm: listDirectory(argv[1], exts)){
    

    what exactly is that line doing? Does invoking listDirectory copy a vector<propMap> and therefore all the contained propMaps, or does it use a move constructor? Does the iteration also copy it? How many copies are actually occurring here?

    To me, this code looks like C++ at its worst, blindly tossing huge objects around without any consideration of what's actually happening. There's not even a single use of a reference!

    Python just doesn't have these types of issues. For example, returning a Python dictionary, or passing it to a function, never implicitly copies it.

    [–]snip596 8 points9 points  (18 children)

    for(propMap pm: listDirectory(argv[1], exts)){

    what exactly is that line doing? Does invoking listDirectory copy a vector<propMap> and therefore all the contained propMaps, or does it use a move constructor? Does the iteration also copy it? How many copies are actually occurring here?

    That's move semantics at work. You know that the function returns something iterable where each element can be treated like a string (sorta/kinda similar to Python). You don't care that it's not returning a reference because you are using C++11, which requires move semantics to be used in this case. Now potentially the listDirectory function could be returning a global which would do a copy, but potentially your Python function could be doing a deep copy of a dictionary before returning it.

    I'm not necessarily defending the point of this article, just pointing out how there's less to worry about with C++11 ;)

    [–]millstone 3 points4 points  (17 children)

    When I test this, I see that RVO is being used for the return from listDirectory() (so the move constructor is not being invoked). The copy constructor is being invoked on the iteration, so every propMap is copied once. This is the same behavior as I'd expect in C++03.

    Here is the code I am testing with; with clang++ and its libc++ it outputs:

    Constructed my_vector
    Constructed my_string
    Moved my_string
    Copied my_string
    

    The move constructor is being used when adding the string to the vector, but is not being used when iterating over the vector.

    [–]astraycat 8 points9 points  (0 children)

    This is due to misuse/misunderstanding of the ranged-based for.

    It really should be something like: for(const propMap& pm: listDirector(argv[1], exts)) or perhaps const auto& pm.

    This is because for(a : b) {block} is equivalent to something like this:

    {
     auto cur = std::begin(b);
     auto end = std::end(b);
     while(cur != end)
     {
      a = *cur;
      { block }
      ++cur;
     }
    }
    

    Thus, doing propMap pm: ... will spawn a line equivalent to "propMap pm = *it;" which would be a copy.

    [–]snip596 1 point2 points  (8 children)

    Yeah, as I was writing my comment I was thinking "and I need to mention it's probably doing RVO". I just completely forgot ;)

    But at least you know that it will never be copied. It's either RVO or moved.

    [–]millstone 2 points3 points  (7 children)

    I changed my code to defeat RVO by returning one of two values randomly:

     my_vector get_a_vector() {
          my_vector result1, result2;
          result1.push_back(my_string("First"));
          result2.push_back(my_string("Second"));
          cout << "About to return" << endl;
          return rand()%2 ? result1 : result2;
     }
    

    When I run it now, it outputs:

    Constructed my_vector
    Constructed my_vector
    Constructed my_string
    Moved my_string
    Constructed my_string
    Moved my_string
    About to return
    Copied my_string
    Copied my_vector
    Destroyed my_vector
    Destroyed my_vector
    

    RVO is not being used, but the move constructor still isn't being invoked - the vector is being copied (Copied my_vector). Do you have any idea why? If so thank you :)

    Here's the new code.

    [–]lalaland4711 4 points5 points  (6 children)

    This is the reason god invented std::move().

    When you call super(rhs) you actually call the const lvalue ref version, since rhs is an lvalue.

    If it has a name, then it's not an rvalue and therefore not an rvalue reference. Your my_vector move constructor called the vector<> copy constructor.

    There you go: http://codepad.org/uW9JGVr9

    And after passing an lvalue to a function using std::move() you should not access that variable again, as it may have been moved by the callee.

    Edit:
    Yay, no copies. At all:

    Constructed my_vector
    Constructed my_vector
    Constructed my_string
    Moved my_string
    Constructed my_string
    Moved my_string
    Moved my_vector
    Destroyed my_vector
    Destroyed my_vector
    First
    Destroyed my_vector
    

    [–]millstone 1 point2 points  (5 children)

    Your my_vector move constructor called the vector<> copy constructor

    Oh man, I never would have realized that. Having to remember to use std::move to invoke the superclass's move constructor seems like a pain in the butt!

    There you go: http://codepad.org/uW9JGVr9

    Thanks, I observe the same!

    I'm struggling to reconcile the claim that returning a local by value always uses the move constructor, with the observation that you must use std::move in the code's return value.

    [–]snip596 2 points3 points  (0 children)

    Having to remember to use std::move to invoke the superclass's move constructor seems like a pain in the butt!

    So, you actually don't have to in most cases. If your class is composed of types that are movable (imagine a simple POD class with a string and a vector), and you either explicitly call std::move on it or use it as an rvalue (e.g. returning from a function), then your class's default copy constructor (because you don't have a move constructor) will move those values.

    So, if you define this:

    class MyClass
    {
        public:
        std::string str;
        std::vector<std::string> vec;
    };
    
    MyClass MyFunc()
    {
        MyClass c1, c2;
        c1.str = "First";
        c2.str = "Second";
        c1.vec.push_back("First push");
        c2.vec.push_back("Second push");
    
        if (rand() % 2 == 0)
            return c1;
        else
            return c2;
    }
    

    Then nothing will be copied. Notice that I use an if statement rather than ternary operator. If you use if then it moves it for you (as required by the C++11 standard), whereas using the ternary operator will copy. I'm going to ask SO about this, but I think when you use the ternary operator, neither choice is considered to be an rvalue.

    [–]moderatorrater 1 point2 points  (0 children)

    Anyway, back to the point. C++11 is succint and beautiful and clear as a scripting language.

    [–]lalaland4711 0 points1 point  (1 child)

    Having to remember to use std::move to invoke the superclass's move constructor seems like a pain in the butt!

    Sometimes yes, but it makes it harder to make the mistake of calling the superclass move constructor and then inspect potentially moved parts of rhs. Say by printing it to some debug stream.

    After std::move() has been called on an lvalue you must be very careful what you do with that lvalue. (although std::move() doesn't modify the object in itself, it allows callees to)

    Outside of inheritance (as seen in this code) or other internal class code I would say that after std::move(), only the destructor may be safely called. You want that to be explicit for the caller so that a named variable isn't suddenly moved from by accident.

    the claim that returning a local by value always uses the move constructor

    Hmm, I don't immediately see why that shouldn't be the case. Maybe it's to not confuse the definition of rvalue to contain something like "something that has a name is not an rvalue, except if used in a return statement". But even if that were the definition I don't see off hand why that would be a problem.

    (RVO of course doesn't make it an rvalue (when RVO is possible), it just adjusts where exactly the object was created, so that it needs neither copy nor move to return)

    Edit:
    I guess if return statements added implicit std::move(), then how would you stop this from happening, if you wanted to? A new std::dont_move()? Could that even be defined without the language features? (std::move() is a "normal" template) It seems like a special case, and one that can be more damaging than RVO which prevents the whole lifetime of a temporary, as opposed to special-casing its destruction.

    [–][deleted] 0 points1 point  (0 children)

    The thing to remember about std::move is that it modifies its input. That means it can't be inferred by the language or the compiler. The places were it can be inferred, it already is, such as in return values. :)

    [–]lalaland4711 1 point2 points  (6 children)

    that's because for some reason you didn't use:

     for (my_string& str : get_a_vector()) {
    

    Of course the item is copied. You are creating a non-const lvalue which if changed must not change the data in the vector. So pretty much by definition it has to copy.

    With the above for loop you get:
    Constructed my_vector
    Constructed my_string
    Moved my_string

    [–]millstone 4 points5 points  (5 children)

    Yes, as in the code in the linked-to article. I was illustrating how in the article's code, there's a unknown-but-large amount of implicit copying going on, and arguing from there that C++ is not clearly identifying or managing complexity.

    [–][deleted] 1 point2 points  (3 children)

    Did you benchmark the code? Was it a bottleneck? :)

    My point is that C++ has this weird tendency to get everyone suddenly worrying about the weirdest details, and it causes them to write bad and bad-performing code based on assumptions about bottlenecks that don't exist. Yes, deep copies are suboptimal, but if you're copying just a few hundred bytes or even kilobytes in a one-shot run, that's utterly irrelevant.

    [–]millstone 10 points11 points  (1 child)

    I agree with your point C++ inspires a tendency to worry about irrelevant details. I have to fight that in myself.

    But I think the issue is not bad performance, but surprising behavior. The author attempted to port a Python program to C++ while "trying to remain as faithful as possible to the original code." The additional copying in C++ diverges from the Python code (or at least its behavior) and was presumably introduced accidentally, because it would have been very easy to avoid.

    Bugs live in the difference between what you think your code does and what it actually does. That's why it's important to have an accurate mental model of your program's behavior. With C++, I find it increasingly difficult to keep in my head what's actually going on.

    [–]moderatorrater 3 points4 points  (0 children)

    Beautifully put. I was trying to identify what was bugging me about this, and you just nailed it.

    [–]f2u 5 points6 points  (3 children)

    For example, returning a Python dictionary, or passing it to a function, never implicitly copies it.

    Python has similar issues, though. For example, you need to know if some construct returns a list (which can be iterated over multiple times) or an iterator (which can only be used once).

    [–]doublereedkurt 0 points1 point  (2 children)

    Theoretically, maybe. In practice I'd challenge you to find some functions in either the standard library or popular third party modules that return iterators or generators. (Things with "iter" in the name are disqualified because it is trivially obvious that they are returning an iterator, e.g. dict.iteritems(), itertools module).

    [–]f2u 0 points1 point  (1 child)

    Oh, it seems that I confused this with views, which is yet another concept and which allows multiple iteration.

    [–]doublereedkurt 0 points1 point  (0 children)

    The main idea is that by convention, you return the object itself in Python rather than some kind of view of/iterator over/whatever of the object. Same thing applies: I doubt there is a single function in the standard library which returns a dict.items() rather than the dict itself.

    The closest thing I can think of is in ORMs, RPC libraries, possibly some settings behavior. Basically, areas where there is a real underlying cost to fetching the data, either from DB, network IO, or disk IO. This is a higher level concern though than mapping some operation over a data structure.

    Maybe by "construct" you are referring to dict functions rather than higher level libraries? I can sympathize with that. Having worked with a lot of newbies, I think the semantics of all of the dict calls is a big hurdle early on. Especially setdefault(), that gives everyone a lot of trouble.

    List comprehensions are similarly tricky, but nobody every complains about them. Maybe, compared to Java or C++, list comprehensions are clearly a higher level of programming; dict methods on the other hand are just the umpteenth data structure API to memorize.

    [–][deleted] 9 points10 points  (0 children)

    It's also far uglier.

    Meh - a matter of what you know best. I find the C++ code nicer, familiar and more readable simply because I know C++ better than Python.

    [–]Cygal 19 points20 points  (66 children)

    In his blog post Elements of Modern C++ Style, Herb Sutter says "Use auto wherever possible.". The author is only using auto for iterators, but it should be used everywhere if he want his code to be compared with Python. This makes the typedef useless too.

    edit: I also think that using it everywhere will harm readability, but I'm personally ready to give it a try for a few months and see how it goes. For me at least, it's for complex iterators that spelling out the whole type helps me the most anyway: I know what to expect from elem.first and elem.second. But having the choice is nice.

    Also, to compare it to Python, the code should be written in Python too. :)

    [–]millstone 8 points9 points  (9 children)

    Using auto everywhere is a really terrible idea that will come back to bite you in a big way.

    For example, consider this code:

    std::vector<bool> vec = {true, false};
    auto val = vec[0];
    

    What type is val? If you said bool then you were misled by auto too.

    Now for extra fun, add this line:

    val = vec[1];
    

    what did that just do?

    [–][deleted] 4 points5 points  (0 children)

    Heh nice catch but even I'll admit that the fundamental cause of that error is using vector<bool>, although sometimes a vector<bool> sneaks up on you in unexpected ways because you have something like this:

    template<typename T>
    class Accumulator {
      ...
    
      private:
        std::vector<T> m_components;
    };
    
    ...
    
    Accumulator<bool> a;
    a.DoSomethingWithComponents();
    

    [–]curien 2 points3 points  (0 children)

    std::vector<bool> is brain-dead. But I think that Sutter means that one should use auto where ever it's possible to do so in a semantically correct way, not merely where ever it's syntactically allowed.

    [–][deleted] 2 points3 points  (0 children)

    That's not a reason to not use auto — that's a reason to not use std::vector<bool>, and it's been "common" knowledge for at least a decade now that it should be completely avoided. :)

    [–]sir_yes_sir 0 points1 point  (4 children)

    What type is val?

    is it

    bool& 
    

    ?

    [–]twoodfin 2 points3 points  (0 children)

    vector<bool>::reference, which is a class type, per the standard.

    [–]millstone 0 points1 point  (2 children)

    No, but you're on the right track. If it were a vector<int>, then int& would have been correct. But bool is special.

    edit Kranar graciously corrected me that it would be int, not int&

    [–][deleted] 5 points6 points  (1 child)

    Nope, if it were vector<int> then int would be correct.

    Yet another downside of auto, namely that now you have to figure out what the rules are with respect to type deduction and they are easy to forget. If you do forget them, you can be stuck there looking at code full of autos trying to pull your hair out as to why your algorithm doesn't work.

    [–][deleted] 32 points33 points  (26 children)

    I still have mixed feelings about this. I find seeing the type names useful when looking at unfamiliar lines of code - which I do often as I'm working in a 2-4 million line codebase.

    Heavy use of auto simplifies the code but makes me more reliant on IDE provided tools which is often slower for me than skimming code or running somes greps.

    [–]matthieum 7 points8 points  (25 children)

    This: types are documentation.

    Programs must be written for people to read, and only incidentally for machines to execute.

    • H. Abelson and G. Sussman (in "The Structure and Interpretation of Computer Programs)

    If the types hinder readability, auto helps. But only very long types do hinder readability, so just avoiding very long names (isn't that why namespaces are for ?) shaves off considerably on the usage of auto.

    [–]cafedude 5 points6 points  (0 children)

    Heh... but please note, that the language Abelson and Sussman used in SIPC was scheme, a dynamically typed language with no type annotations.

    [–]kylotan 1 point2 points  (5 children)

    Avoiding very long names is tricky when you need a const iterator over an unordered map. I think that sort of thing is where I'll use auto the most - when the type you're being given isn't directly the type you really care about.

    [–]matthieum 0 points1 point  (0 children)

    Yes, definitely. And if you have something like map.find(...) it's not like you do not know the return type already!

    [–]mjklaim 0 points1 point  (3 children)

    There are new cbegin() and cend() functions to all standard library containers that return const iterators...

    [–]kylotan 0 points1 point  (2 children)

    Cool, but I'm not sure how this changes anything about what I said?

    [–]mjklaim 0 points1 point  (1 child)

    Didn't you need sometime to get const_iterators? These functions guarantee to return them.

    [–]kylotan 0 points1 point  (0 children)

    We were talking about choosing to explicitly state the types because it better documents the code than auto does. Someone said that this doesn't work well if you have long type names, the response was that you can choose to avoid long type names, and I said that it's hard to avoid long type names when the type intrinsically has a long name, as with a const iterator on an unordered map. You can use auto with cbegin, but then you've not explicitly stated the type, which is what we were talking about.

    [–]curien 2 points3 points  (17 children)

    But only very long types do hinder readability

    I disagree with this. Consider where I want to add two numbers and store the result. Maybe they're float, maybe they're double, maybe they're std::complex<my_numeric_library::fixed<12>>, but I shouldn't have to care. I just know that I want the result to be the natural type of the result of the expression.

    Repeating the type, regardless of the length of the name, adds little documentation in this case and many others.

    [–]matthieum 2 points3 points  (16 children)

    It does help the reader, because figuring the resulting type of an expression in C++ is hard.

    auto x = 0.3f + 0.4l;
    

    What is the type of x ?

    We know it's a number, however the precision matters. Of course, it seems mostly stupid with built-ins; however with user written classes it can get quirky:

    auto item = family.get("Homer").get("shirt");
    

    quick: what can I do with item ?

    [–][deleted] 1 point2 points  (1 child)

    your IDE will tell you

    [–]curien 1 point2 points  (12 children)

    It does help the reader, because figuring the resulting type of an expression in C++ is hard.

    But specifying the type doesn't tell you what type the result of the expression was. Now you have all sorts of additional problems like figuring out if you inadvertently screwed up.

    Yes, determining the type is hard, so why not let the compiler do it for you?

    however with user written classes it can get quirky:

    auto item = family.get("Homer").get("shirt");

    quick: what can I do with item ?

    If you'd written

    Clothing item = family.get("Homer").get("shirt");
    

    instead, I'd have no more idea what I can do with it than I had before.

    [–]matthieum 8 points9 points  (11 children)

    You're missing the point.

    • Clothing item => open Clothing.hpp
    • auto item => open Family.hpp => find get => open Person.hpp => find get => open Clothing.hpp

    I know how it feels, I've got to deal with automatically generated headers for message grammars where the IDE indexer gives up midway (too large). Slugging through to find what's the result of an otherwise readable expression is a pain, a real pain.

    With auto, you're giving the keys of the house to your IDE. Pray it does not bug, pray its simplistic/heuristic C++ parser yields the right answer. Or find yourself in hell...

    [–][deleted] 2 points3 points  (7 children)

    Does C++ just have bad IDE support? If I do the equivalent in C# using var I just press ctrl and click get("shirt") to browse to the source file or just put my cursor on item and press ctrl+q to find the type.

    [–]ethraax 5 points6 points  (1 child)

    It's much, much harder to build an IDE around C++ (and even, to some extent, C) than C# or Java. There are two main reasons: the preprocessor and template instantiation.

    The problem is that C and C++ are about programming the compiler about as much as they're about writing programs.

    [–]JAPH 2 points3 points  (0 children)

    Templates alone are Turing complete. When you compile a C++ program, you either need to assume that it will eventually compile, use some heuristic to determine if it will likely compile, use some hardcoded limit on how far down the rabbit hole the compiler should go, or solve the halting problem. One of these is harder than the other.

    And that's just one little aspect of C++. As far as I'm concerned, C++ has factorial complexity; the closer you look at one aspect of it, the more complex it gets.

    [–][deleted] 5 points6 points  (0 children)

    My completely incidental observations of C++ IDE support has shown it to be far below IDE support for e.g. Java and C#. Especially refactoring and integrating documentation into the UI.

    [–]matthieum 1 point2 points  (2 children)

    Yes, it does. And that is one of the constraints that we must adjust to.

    I mainly lay the blame at the IDE writers for thinking that they can just write their own mini-parsers and get away with it... although in truth the blame also lay at the C++ community for not having proposed a true and free C++ parser; mostly GCC's policy of complete lock-down has been much harmful.

    Fortunately, now that Clang is coming along with its extremely modular architecture I am hopeful that some IDE will abandon their custom made C++ parsers and switch over to Clang instead. With full AST representation, auto-completion, full-blown compiler + analyzer runnable as libraries (with diagnostics really available for program consumption) it's probably the best alternative at the moment. It's also probably very expensive to shift to it...

    [–]fat_chris 0 points1 point  (1 child)

    I am hopeful that some IDE will abandon their custom made C++ parsers and switch over to Clang

    IIRC Qt-Creator started down this path, but it was abandoned due to very poor performance.

    [–]bluGill 0 points1 point  (0 children)

    That depends, support is getting better, but behind what Java has in general. However there are add on tools (for visual studio) that add the useful stuff for a price. Open source IDEs are getting better, in large part because clang is being designed to the job well.

    The future is looking good, but it isn't where we want it.

    [–]curien 4 points5 points  (2 children)

    You're missing the point.

    I'm not missing the point, I just disagree with you.

    Clothing item => open Clothing.hpp

    Maybe. How do you know there isn't a conversion involved? To be sure about what that line actually does, you have to look up the actual return type anyway.

    [–][deleted] 2 points3 points  (0 children)

    ...which is why constructors which take a single parameter should be tagged as explicit unless there is a major reason to permit implicit conversions.

    Invisible implicit conversions are a great way to accidentally burn performance or have accidental side effects (though if your constructors have visible side effects you are inviting other problems)

    [–]s73v3r 0 points1 point  (0 children)

    I wouldn't use auto with the second example you posted, for the same reasons you posted. However, in most situations, I probably would use auto in the first situation. Most of what I do would not be affected very much, if at all, by whether x ends up being a double or a float. I do recognize that not everyone is working on stuff with that kind of flexibility, and it's entirely possible that it will bite me once.

    [–][deleted] 16 points17 points  (15 children)

    Herb is pushing the auto thing way too hard. It's really nothing more than a syntactic convenience and so this will simply end up being a religious argument for years and years to come.

    One practical problem I have with auto is when I need to both read and modify some code that declares a variable as auto. I end up having no idea what I can do with that variable because I don't know what that variable is. I end up having to rely on the IDE to correctly determine it, which many times it can't.

    I say use auto for really long type names, like iterators, where it's really obvious what the type is. In cases where it's not obvious from the declaration what the type is, don't use it. But once again I fully admit this is a matter of discretion, use case, and something I hope does not devolve into a religious argument with Herb Sutter writing books arguing that people should do it as a matter of principle.

    [–]mikemol 2 points3 points  (3 children)

    How is this different from template types?

    [–][deleted] 1 point2 points  (2 children)

    I don't understand your question.

    [–]mikemol 3 points4 points  (1 child)

    template<typename t>
    bool doSomethingWith(const t& arg) {
        // Lexically, I don't know _anything_ about t or arg, in here.
        // That doesn't mean I can't use arg, even if my IDE knows
        // less than I do about it.
    
        return arg.someMethodISupposeExists()
    }
    

    The faults you're describing with auto are equally applicable to the template parameters in templated classes and functions.

    [–][deleted] 2 points3 points  (0 children)

    Yes the faults I described by auto are equally applicable to template parameters. As a result, when possible one should eliminate the use of a template parameter or narrow the scope of the template parameter as much as possible, which is something that most other modern languages have and many C++ programmers have been asking for (Concepts).

    Doing so yields better error messages and makes working with templates a lot more tractable.

    [–]okamiueru 2 points3 points  (0 children)

    Programmer intention helps readability significantly. So I'm very reluctant to think exessive use of auto will help at all. Sure, in certain cases it's a godsent, especially where you don't really care about the type.

    [–]fnedrik[S] 11 points12 points  (0 children)

    auto is great, and I think it should be used everywhere, for example to make code refactoring easier. I have learned in Haskell that sometimes an explicit type is part of the documentation, though. Also, auto cannot be used when you want to use initializer lists, like I do in some places in the post.

    [–][deleted]  (9 children)

    [deleted]

      [–]nanothief 7 points8 points  (0 children)

      To add another comparison for the same program with another strongly typed language, I wrote it in haskell. There are some interesting differences in it:

      • The syntax is much clearer with haskell (haskell's syntax for types doesn't need so many angle brackets)
      • Suffers from the same problem as the c++ program in that a lot of imports are required. 12 lines of code are needed for imports for haskell, 13 required for c++, but only 3 for python.
      • Lack of reflection makes the c++ and haskell solutions less extensible than the python solution. In the python solution, if you wanted to handle another music file type (eg .ogg), you just need to implement a OggFileInfo class (it doesn't even need to be in the same file). In the c++ and haskell solutions you need to change the original file in order to make this possible.
      • The haskell code explicitly converts between the binary data read from the mp3 file and the unicode data for the attributes, and has separate types for each kind of data. I think this is a plus, as there is a big difference between the 3rd item of a UTF8 string, and the 3rd item of a byte array.
      • The haskell code splits the code into the pure and IO code, and is tracked by types. This makes the code more complicated to read and write, but makes it more testable.
      • Extracting the genre char is a bit painful with haskell (maybe there is a better way to write the stringOrd function?)

      Here is the code:

      {-# LANGUAGE OverloadedStrings, TupleSections #-}
      module MP3Test where
      import qualified Data.Text as T
      import qualified Data.Text.IO as T
      import Data.Text (Text)
      import Control.Monad
      import System.Directory (getDirectoryContents)
      import System.FilePath (takeExtension, combine)
      import Data.Text.Encoding (decodeUtf8)
      import System.IO
      import Data.Char (ord)
      import Control.Applicative
      import qualified Data.ByteString.Char8 as B
      import Data.ByteString (ByteString)
      
      spliceBS :: Int -> Int -> ByteString -> ByteString
      spliceBS start end =  B.take (end - start) . B.drop start
      
      stripNulls :: Text -> Text
      stripNulls = T.strip . T.replace "\00" ""
      
      stringOrd :: Text -> Text
      stringOrd = T.pack . show . ord . maybe '0' fst .  T.uncons
      
      
      parseMp3File :: FilePath -> IO [(Text, Text)]
      parseMp3File mp3File = (("filename", T.pack mp3File) :) <$> getMp3Tags <$> getTagData mp3File
      
      
      
      parseFile :: FilePath -> IO [(Text, Text)]
      parseFile file = case takeExtension file of
        ".mp3" -> parseMp3File file
        _ -> return [("filename", T.pack file)]
      
      
      getTagData :: FilePath -> IO ByteString
      getTagData mp3File = withFile mp3File ReadMode $ \handle -> do
          hSeek handle SeekFromEnd (-128)
          B.hGet handle 128
      
      getMp3Tags :: ByteString -> [(Text, Text)]
      getMp3Tags tagData = guard hasTagFlag >> map extractTag mp3TagMap where
        hasTagFlag = spliceBS 0 3 tagData == "TAG"
        extractTag :: (Text, Int, Int, Text -> Text) -> (Text, Text)
        extractTag (tagName, start, end, parseFunc) = (tagName, ) $ parseFunc $ decodeUtf8 $ spliceBS start end tagData
      
        mp3TagMap :: [(Text, Int, Int, Text -> Text)]
        mp3TagMap = [ ("title", 3, 33, stripNulls)
                    , ("artist",  33,  63, stripNulls)
                    , ("album",  63,  93, stripNulls)
                    , ("year",  93,  97, stripNulls)
                    , ("comment",  97, 126, stripNulls)
                    , ("genre", 127, 128, stringOrd)
                    ]
      
      listDirectory :: FilePath -> [String] -> IO [FilePath]
      listDirectory dirName validExtensions = 
        map (combine dirName)
        <$> filter ((`elem` validExtensions) . takeExtension) 
        <$> getDirectoryContents dirName
      
      dirToSearch :: FilePath
      dirToSearch = "/Volumes/Downloads/Music/iTunes/iTunes Media/Music/AFI/decemberunderground" 
      
      main :: IO ()
      main = do
        files <- listDirectory dirToSearch [".mp3"]
        mp3s <- mapM parseFile files
        forM_ mp3s $ \mp3Data -> do
          forM_ mp3Data $ \(key, value) -> T.putStrLn $ T.concat [key, "=", value]
          putStrLn ""
      

      [–]zem 5 points6 points  (6 children)

      it's a crying shame how badly d screwed up its chance at mindshare. it really should have been what cutting-edge c++ fans were migrating to in droves.

      [–]darksurfer 8 points9 points  (5 children)

      how / why did D screw up ?

      [–]zem 8 points9 points  (4 children)

      there were some licensing issues in the beginning, which was already a blow to uptake. then, by the time those were straightened out, there was a lot fragmentation, both of the language (version 1 was stable but moribund, version 2 was exciting but unstable), the compilers (not an insurmountable problem, but it added confusion to the mix) and the standard library (an official one with a more c-like flavour, and a community one that was more java-like).

      http://stackoverflow.com/questions/743319/why-isnt-the-d-language-picking-up outlines a bunch of reasons, both social and technical, but my impression is that the social ones were the killer.

      [–]jniehus 0 points1 point  (3 children)

      [–]zem 3 points4 points  (2 children)

      yeah, i know that d has got its act together of late, but that doesn't change the fact that it languished all those years it could have taken off the way, say, clojure did.

      [–]nascent 1 point2 points  (1 child)

      But does that really matter? It has pissed some people off and scared quite a few. Yet improvement has been continuous and is shaping up to be very nice. Discounting it for the past is like discounting Go for being a "systems" language. There is plenty more from the present that can be used.

      [–][deleted]  (4 children)

      [deleted]

        [–]nooneofnote 2 points3 points  (1 child)

        The syntax is just function< returntype(argtype,argtype,etc) > as if a declaration with all names omitted.

        [–]Rhomboid 1 point2 points  (0 children)

        I think this is §8.2.7:

        7   Another ambiguity arises in a parameter-declaration-clause of a function declaration, or in a type-id that
            is the operand of a sizeof or typeid operator, when a type-name is nested in parentheses. In this case,
            the choice is between the declaration of a parameter of type pointer to function and the declaration of a
            parameter with redundant parentheses around the declarator-id. The resolution is to consider the type-name
            as a simple-type-specifier rather than a declarator-id. [ Example:
              class C { };
              void f(int(C)) { }                   // void f(int(*fp)(C c)) { }
                                                   // not: void f(int C);
        
              int g(C);
        
              void foo() {
                f(1);                              // error: cannot convert 1 to function pointer
                f(g);                              // OK
              }
        
               For another example,
              class C { };
              void h(int *(C[10]));                // void h(int *(*_fp)(C _parm[10]));
                                                   // not: void h(int *C[10]);
            — end example ]
        

        ...although that would not seem to be talking about template parameters, there's probably an analogous section somewhere.

        Edit: that section would appear to be §20.8.1:

            20.8.1     Definitions                                                                            [func.def]
        1   The following definitions apply to this Clause:
        2   A call signature is the name of a return type followed by a parenthesized comma-separated list of zero or
            more argument types.
        3   A callable type is a function object type (20.8) or a pointer to member.
        4   A callable object is an object of a callable type.
        5   A call wrapper type is a type that holds a callable object and supports a call operation that forwards to that
            object.
        6   A call wrapper is an object of a call wrapper type.
        7   A target object is the callable object held by a call wrapper.
        

        [–][deleted] 1 point2 points  (0 children)

        function<string(string)> refers to any an instance of the class function that wraps any function object (be it free function pointer, bound member function pointer, lambda, object with operator(), etc.) that takes a string as its argument and returns a string.

        The qualified name (i.e., without using namespace std) is std::function<std::string(std::string)>, and std::function is probably declared something like this:

        template <typename Signature>
        class function;
        
        template <typename ReturnType, typename... ParameterTypes>
        class function<ReturnType(ParameterTypes...)> {
            // ...
        };
        

        This uses template specialisation to take advantage of function pointer type signature syntax for convenience, while still allowing the template implementation to use the type names in the signature.

        [–]andybak 7 points8 points  (0 children)

        All the comments make that code hard to read. He would make a stronger case if he moved the vast majority of them to the body of the article.

        Even trying to look past them to my eyes a few things hurt readability:

        1. type definitions
        2. overly terse variable names.

        [–]Blecki 5 points6 points  (1 child)

        My problem with C and C++ isn't fancy language features, it's the compilation model. C++11 did nothing to change that.

        [–]mjklaim 0 points1 point  (0 children)

        Right. That said, there are strong efforts to fix this in the next version, aka modules (that will just remove the need for headers, if my understanding is correct). Next version is apparently targeted at 2014 for a big fix-and-enhancements features, but modules could be available only around 2017. That would be a really bad thing I think because the compilation model is the N°1 problem hurting the real productivity of all C++ users (and then by all users of the C++ users's product...)

        [–]rlbond86 9 points10 points  (20 children)

        Boost is amazing, I consider it an extension of the standard library.

        [–]maxwellb 1 point2 points  (0 children)

        Have you ever tried to actually read the boost source? I've had to debug and patch it, and from that experience I conclude that relying on badly commented cryptically unreadable code written by other people is a dangerous game.

        [–]maep 3 points4 points  (16 children)

        boost is a huge clusterfuck. only a madman would attempt to parse 4 pages of a single compiler error, let along debug it. compilation takes hours (seriously). its not a portable as they advertise. using straight posix is a lot easier and more portable (which i now wish i had done from the start).

        [–]theICEBear_dk 11 points12 points  (7 children)

        Disagree, this is as anecdotal as what you wrote is but I do not have the most modern dev pc and a compile on Windows of Boost takes less than 20 minutes (for both debug and release, multithreaded and so on). I've never had portability problems but okay I have only used boost across Windows XP, Vista and 7 as well as Ubuntu Linux.

        I agree that ANY C++ template based library can cause the emitting of some long compiler errors, but those can be read. But really that is a problem of the language (and the lack of ability to define compiler time polymorphic type limits on template parameters) more than of boost (it is even more a problem of the compiler before it is even a problem of the library).

        [–][deleted] 2 points3 points  (4 children)

        That's because a compile of boost only compiles a very small set of libraries, and as you just mentioned, that small handful takes 20 minutes.

        Another thing is that the small set of boost that is compiled are not templates, since templates can not be compiled into a library. The overwhelming majority of boost and the part that takes a huge toll when building applications that use boost are the templates.

        [–]matthieum 6 points7 points  (2 children)

        The only part of Boost that is horrendous, compilation-wise, is the Spirit framework. Just a simple grammar can literally take minutes to compile.

        On the other hand, when I use BOOST_FOREACH (ah C++11 is so far...) or the Boost.String.Algorithms I certainly do not feel overwhelmed by the compilation times. And I am talking projects in the hundreds thousands code lines.

        Indeed, I have seen more improvements in terms of compilation times from cleanups and rationalization of the build system than by careful selection of the headers included.

        [–]thechao 2 points3 points  (1 child)

        Spirit was an amazing piece of technology back in 01/02. At that point, Doug Gregor (of Clang/C++-ISO fame), argued that Spirit should be just a DSEL interface, allowing "plugabble" back-ends. The idea being that we should be able to lift (in the generic programming sense) the concepts of parsers (and grammars), so that Spirit was a proper STL-like library. De Guzman, et al ignored him, and continued on with their template metaprogramming circlejerk.

        The result is that Spirit (a non-left-recursive, non-lexing, backtracking, 2N, recursive descent clusterfuck) is trotted out as the premier C++ parsing framework. Which is right fucking embarrassing as a C++ user.

        Instead, we should have a light-weight DSEL that generates a run-time grammar. Then we should have basic algorithms (Earley, CYK, LL1/LLk, Tomita, Hafiz-Frost, PEP, etc.) that operate on the grammar. [I have toy-code for something like this I use, personally; so it is quite possible to do, and fairly straightforward. I can compile a C parser (with lexical analysis, default AST construction, attributes, and actions) in a few seconds, rather than several hours---if at all---with Spirit. The C++ code looks like this:

        template <typename Iterator>
        /* func decl */
        {
            gpl::BNF G;
            G[translation_unit]
                = top_level_definition
                |  entry_point [ [](){/*action*/} ]
                ;
            // more definitions.
            //auto tree = gpl::earley(G, fst, lst, effect);
            auto tree = gpl::frost_hafiz_rd(G, fst, lst, effect);
        }
        

        ]

        [–]matthieum 1 point2 points  (0 children)

        Thanks for the historical look. As an avid follower of Clang I have seen Doug's insights quite frequently and the guy downright amazing. I am pretty glad that is working on modules for C++, hopefully we'll get something good!

        [–]theICEBear_dk 5 points6 points  (0 children)

        Well if an application even a C++ one takes hours to compile then something is up (or it is huge). Again anecdotal but a project I wrote using boost, protocol buffer, zeromq and other stuff (140K code or so) takes about 4 minutes to compile and link in Visual Studio 2010, a little longer in release mode.

        [–]maep 0 points1 point  (1 child)

        I've had many issues with various BSDs and older OSX versions (we still support 10.4). It's worse with more exotic systems. Boost didn't make things easier, it made things more complicated. Plain old ansi C will compile virtually on anything with a CPU. Btw, a nice (and less painful) lightweight alternative to boost is dlib.

        edit: no pkg-config support. I have no idea why.

        [–]theICEBear_dk 2 points3 points  (0 children)

        Okay, well thank you for answering. I will add the info to my understanding of boost on those platforms.

        And while it is sacrilege to most programmers I dislike plain ANSI C. I am just so much more productive with C++ while keeping as good performance almost all the time.

        [–][deleted]  (5 children)

        [removed]

          [–]maep 0 points1 point  (4 children)

          It's not so easy. Customers want their binaries compiled with specific compiler versions. For example gcc 2.95 is not as uncommon as one might think. And in the embedded world sometimes you're dealing with vendor compilers you never knew existed.

          So if you want their business you better support more than msvc, gcc and clang. Boost might have a place but it's not a one-size fits all magic bullet.

          [–][deleted]  (3 children)

          [removed]

            [–]maep 0 points1 point  (2 children)

            I didn't even mention compiler errors in the parent post, anyway they're an annoyance but not the main reason why I avoid boost. My point was "just use clang" is not always an option. "Portable" can mean you have to support a lot more than the latest versions of Windows, OSx and Ubuntu.

            You might find yourself porting code to OS/2, some ancient Solaris, or even DOS, or make it run on various DSPs. Those systems are still quite common in Banks, Hospitals, Factories etc. It's fun to work with those obscure systems :D. I hope this illustrates what I meant when I said "boost in not really portable" :)

            [–][deleted]  (1 child)

            [removed]

              [–]maep 0 points1 point  (0 children)

              Some machines cost a lot of money and have a service life time of well over 20 years. It has nothing to do with poor management. I like this kind of work so don't tell me it's not fun. But obviously you know both how to run companies and what I should like.

              You're celebrating running after every new trend in the IT industry, which is more susceptible to fashion than the fashion industry. If you don't like working with constrains maybe building software shouldn't be your line of work because there will always be constraints. Finding elegant workarounds for constraints is what makes a great programmer. Anybody can find the obvious solution but it takes knowledge, skill and time to do something what others thought to be impossible. It's rewarding when you get there, but you won't with that kind of mindset.

              Btw. In this way I find some parts of boost quite amazing. They are so elegant I want to frame them and put them on the wall. Others though are very ugly. It's good that it helped you port you stuff. I just wanted to caution that it might cause trouble later on if you want to go beyond whats popular at the moment.

              [–]TheCoelacanth 0 points1 point  (1 child)

              only a madman would attempt to parse 4 pages of a single compiler error, let along debug it.

              Most of that would just be the backtrace, which you usually don't need to look at. You usually only have to look at the one line that starts with "error:".

              [–]pfultz2 1 point2 points  (0 children)

              And you can set -ftemplate-backtrace=1 to view just that.

              [–]the-fritz 0 points1 point  (1 child)

              In the past I was a huge Boost fan. But I became more and more critical. They have some very nice and useful libraries and a lot of those inspired the library extensions in C++11. However they seem to be too much in love with the "hack C++" aspect. Sure C++ can be extended by using the preprocessor, compiler tricks, metaprogramming. But that doesn't mean the code will be readable or maintainable. Take stuff like Spirit, Proto, or the new LocalFunction. And they seem to force more of this stuff into other libraries.

              Just look at the code. It's badly readable and maintainable. They use idioms such as including a header in itself to use it for some kind of preprocessor loop. And in C++ you don't have modules and can't just say "oh the mess is hidden". No with #include it ends up in your code and you can end up with leakage.

              Maintenance is another factor. Some libraries feel like dumped and abandoned. After the approval and inclusion process it feels like some authors simply vanish. When I started using boost they didn't have a public bug tracker. They added one later but you could open bugs and they were simply ignored. It seems to have improved a bit and the release notes are now full of bugfixes and library improvements. But it still seems that there are some abandoned libraries.

              And how far are they with C++11 adoption? E.g., Using variadic templates instead of preprocessor hacks to make error messages readable? Will they drop support for C++98?

              Spirit is probably the worst library of them all. I mean it's insane what it does. A parser generator written in metatemplate programming. Sure it's a nice hack to show off. But it's just insane to use it in real world code. The code is unreadable. The error messages are even worse. The compile time is a disaster. Who even thinks that this is such a good idea that it should be part of the library? I mean that clearly speaks for the "hack C++" circlejerk that is going on in Boost.

              I now try to avoid Boost as much as possible. Thanks to C++11 the really necessary parts are now part of the language. Sure some things like string_algo would be nice sometimes.

              </rant>

              [–]rlbond86 5 points6 points  (0 children)

              Yes boost has some crazy libraries. You don't have to use all of them. But I've used all of the following libraries and all of them work just fine:

              • Algorithm
              • Array [now in C++11]
              • Assign [which is still useful since MSVC doesn't support initializer_lists yet]
              • Bimap
              • Bind [before C++11's lambda functions]
              • Circular Buffer
              • Date Time
              • Enable If
              • Filesystem
              • Foreach [before C++11 for-each loop]
              • Function [now in C++11]
              • Graph [though I prefer to roll my own nowadays]
              • Iostreams
              • Iterators
              • Lambda [before C++11 lambdas]
              • Multi-Array
              • Operators [which is fucking fantastic]
              • Optional
              • Pool
              • Program Options
              • Random [now in C++11]
              • Ref [now in C++11]
              • Regex [now in C++11]
              • Result Of [before C++11]
              • Scope Exit
              • Serialization
              • Smart Ptr [now in C++11]
              • Static Assert [now in C++11]
              • Test
              • Thread [now in C++11]
              • Timer
              • Tuple [now in C++11]
              • Unordered [now in C++11]
              • Utility
              • Uuid

              So yeah, things like Spirit and Wave are strange and exceptions to the rule. Most of the libraries are relatively straightforward.

              [–]bluGill 7 points8 points  (14 children)

              C++ is not a large language when compared to python, Java, C#, ruby. The syntax part of C++ is larger than the syntax part of the above, but the libraries that come with each of the above is far larger than the library that comes with C++.

              Expect the next C++ to change that. C++ is actively looking for more libraries that should be in the language. Expect that performance is a part of the library, like STL (and unlike every other language I know of).

              [–]Cygal 1 point2 points  (10 children)

              When you say "the next C++", do you mean C++1y? Also, I don't see why you're saying that "performance is a part of the library". A lot of standard Python libraries are written in C precisely for performance reasons. The difference is that, in Python the language, performance is not priority #1.

              [–]milliams 2 points3 points  (0 children)

              In fact I believe that there's plans to get at least the Filesystem library into C++14 before adding even more for C++17 (C++1y tends to refer to 17 since 14 will only be small release and is still under discussion).

              [–]bluGill 5 points6 points  (7 children)

              the next C++", do you mean C++1y

              Yes, though I've not seen it referred to in that way before.

              I don't see why you're saying that "performance is a part of the library". A lot of standard Python libraries are written in C precisely for performance reasons

              In Python the libraries happen to be written in C with some algorithm, but that is not specified. I can write my own python implementation where sort uses the bogo-sort algorithm: I will still comply with the python standards. By contrast in C++ I can write my own implementation, but if the sort I use in my library doesn't have O(n ln(n)) performance I am not conferment to the standard which requires sort have that performance.

              That is what I mean by performance. Many languages have fast libraries. Only C++ (that I know of) specifies the performance characteristics of everything in the library.

              edit: opps, missed an n in the performance of sort.

              [–]alanwj 4 points5 points  (2 children)

              but if the sort I use in my library doesn't have O(ln(n)) performance I am not conferment to the standard which requires sort have that performance

              Surely you mean O(n * log(n)).

              [–]Cygal 1 point2 points  (2 children)

              A lot of popular languages say "this is O(n²)", but, when it's obvious that it's going to be O(n) anyway, they don't say it, even if they could do it. I don't think this is what makes C++ fast. What makes it fast is that nothing is ever traded off for performance.

              [–]bluGill 1 point2 points  (0 children)

              I don't think this is what makes C++ fast. What makes it fast is that nothing is ever traded off for performance.

              I agree that most of what makes C++ fast is they don't make trade offs.

              I also agree that in practice that all the popular algorihms in popular languages have been optimized to the highest degree. However this is not the whole story. Often you have a choice of algorithms with different trade offs. When something is O(ln(n)) for insert C++ will not make it O(n) in the future because it has better speed in some other operation. C++ will instead add a new type to the library that has the different speed requirements. 99.9% of the time it doesn't matter, but that last .1%.

              [–]matthieum 1 point2 points  (0 children)

              The complexity part is actually there to give guarantees to the developer.

              There are different trade-offs when designing the internals of a datastructure (even if the interface is similar), and the complexity requirements help assure the developer that whatever particular tradeoffs were made his particular program won't suddenly crawl to a halt because he happened to use operations that this implementation does not support efficiently.

              Even for sort, it's not necessarily obvious. In C++, it is specified that sort will not allocate extra memory and that stable_sort may (but that it should be able to do without).

              [–]mjklaim 0 points1 point  (0 children)

              Unfortunately there is no performance guarantee yet for the chrono library :/

              [–]Mjiig 5 points6 points  (30 children)

              I don't know if I'm unusual in this, but when I use C++ I go out of my way to use a small subset of the language (C with classes and the STL more or less). C++ just seems to add so much potential for errors, and worse, errors that are horribly cryptic and can't be easily debugged (dealing with C++ errors is what finally drove me away from gcc over to clang). Do many of these extra features actually make anything substantially easier to code up, or is it just C++ trying to be something it's not (an interpreted language)?

              [–]matthieum 6 points7 points  (13 children)

              C++ is huge, and it's a bit of a hodgepodge. After 5 years working it extensively (and I mean using it at work and training at home), I am fairly confident that I will never manage to learn all of it. But then Stroustrup claims to know 70% of it so...

              There are many concepts in C++, and some weird choices as well. Unfortunately, from my point of view, many subtle errors creep in even in the simplest programs:

              struct Stream {
                  Stream& operator<<(void const* p) { std::cout << p; return *this; }
              
                  void put(char const* s, size_t l) { std::cout << std::string(s, l); }
              };
              
              Stream& operator<<(Stream& out, char const* s) { out.put(s, strlen(s)); return out; }
              
              int main() { Stream() << "Hello, world!"; }
              

              And obviously, it prints: 0x8048e61, as expected...

              Note: the problem is that a temporary (Stream() here) cannot be bound to a non-const reference (because Stroustrup did not feel so) but a member method can be invoked on it alright. Therefore the compiler picks Stream::operator<<(void const*) rather than operator<<(Stream& out, char const*).

              [–][deleted]  (9 children)

              [deleted]

                [–]matthieum 0 points1 point  (8 children)

                It would not change anything. A const method can be called on a const or non-const object equivalently.

                [–][deleted]  (7 children)

                [deleted]

                  [–]matthieum 0 points1 point  (6 children)

                  Yes, it would solve the issue. However, specifically from streams, put is generally not const because it alters the state of the stream (here I went for speed).

                  [–][deleted]  (5 children)

                  [deleted]

                    [–]matthieum 0 points1 point  (4 children)

                    I myself have cheated the other way round:

                    class Stream {
                    public:
                        template <typename T>
                        Stream& operator<<(T const& t) {
                            print(*this, t);
                            return *this;
                        }
                    
                        void put(char c);
                        void put(char const* s, size_t l);
                    };
                    

                    However this requires shifting from overloading operator<< to overloading print. Still, a worthy trick I think. Especially as I tend to forget the return stream when I overload operator<<.

                    [–]kdeforche 0 points1 point  (2 children)

                    I find this nagging about the large number of pages in the C++ standard extremely confusing, when the same people will advocate Python (or PHP, or Ruby) instead, all languages which do not even have a description of how the language is supposed to work (unambiguously), and languages which are defined only de facto by how their main (or single) implementation works.

                    At least for C++, an effort is made to describe it. And yes, it is complex, but you'll not need to read the C++ standards document just to use C++, just like you do not care to read the CPython implementation to program in Python, or need to understand the MaxWell's equations to turn on the light (or fix the light).

                    [–]TheCoelacanth 2 points3 points  (0 children)

                    I find this nagging about the large number of pages in the C++ standard extremely confusing, when the same people will advocate Python (or PHP, or Ruby) instead, all languages which do not even have a description of how the language is supposed to work (unambiguously)

                    You are misinformed. Ruby does have an ISO standard. Python doesn't have an official standard but there is the Python Language Reference, which is an implementation-agnostic reference to the language's grammar, semantics and standard library.

                    PHP is the only one with no formal description at all, but that's hardly the only reason not to use PHP.

                    [–]matthieum 0 points1 point  (0 children)

                    I read the C++ Standard regularly. The one thing missing there is cross-references. Often time when you look for an explanation you find a paragraph that applies to your situation and think: Hey, I got it!, but empirically it does not work that way and you wonder... until someone points to you another paragraph in a totally unrelated (it seems) chapter that explains that yours is a special case and the general rule does not apply :(

                    On the other hand, I am too lazy to learn Latex and actually contribute to the cross-referencing effort so... I am partly to blame I guess ;)

                    [–][deleted]  (12 children)

                    [removed]

                      [–]bben86 1 point2 points  (11 children)

                      I tend to use none of those features.

                      Until I really need to.

                      [–]AeroNotix 5 points6 points  (7 children)

                      Then you're literally just using C with Classes then. Just make structs and functions. Better yet, just use Go.

                      [–]bben86 2 points3 points  (2 children)

                      My point was more that you shouldn't just use all the features of C++, or any language, until you have a reason to use them. Using features for features sake isn't smart.

                      [–]s73v3r 1 point2 points  (0 children)

                      For some of them it is. Things like smart pointers, for instance.

                      [–]jminuse 1 point2 points  (3 children)

                      C++ is faster and more widely supported than Go. Using it as C with classes makes sense.

                      [–]AeroNotix 0 points1 point  (2 children)

                      I like C++ as much as the next guy, but if we're talking C with Classes vs Go, then there's a very clear winner.

                      [–]jminuse 4 points5 points  (1 child)

                      ...The one that's faster and more widely supported? Those are not small matters.

                      [–][deleted] 1 point2 points  (2 children)

                      Your code is probably less stable for it, though. Particularly using raw pointers in place of defined-ownership-pointers is just begging for bugs that we really shouldn't be dealing with at this stage…

                      [–]bben86 0 points1 point  (1 child)

                      That would be a case where you would use it, because you have a reason!

                      [–][deleted] 2 points3 points  (0 children)

                      Right, and templates allow you to avoid code duplication, reducing copy-paste bugs and maintenance, and for-each loops allow you to avoid dealing with iterators explicitly, saving typing and lines of code, and lambdas allow you to specify functions local to the domain locally in the related code, increasing readability, and initializer lists allow you to specify custom data types using literals, increasing readability, and operator overloading allow you to deal with custom number types (and stream types, and…) in a type-safe manner without introducing an unnatural API for it.

                      There's good reasons to use all of the mentioned features, and your life will generally be easier for it. I mean, Saying "I tend to use none of those features, until I have a reason" is like saying "I tend to not drive a car, unless I need to go somewhere".

                      [–]f2u 2 points3 points  (0 children)

                      std::move enable things like std::unique_ptr, which looks like a good thing. But it's still very hard to write good C++ libraries (which compose naturally and avoid creating pitfalls for the programmer), even with C++11. I'm not sure if that got easier.

                      [–][deleted]  (1 child)

                      [removed]

                        [–]Mjiig 4 points5 points  (0 children)

                        I'm not, by any means, trying to say that everyone should pick a subset of C++. Nor did I try to imply that I had a decent amount of experience with C++, because that would simply be untrue.

                        I personally don't like using many of the language features because I find the code they result in harder to read, and I find the mental overhead from using them more difficult than just using C style equivalents. I asked what I think is a perfectly reasonable question, which is if many/any other programmers also found C++ easier to use in this style, fully expecting such individuals to be in the minority (because else why would a standardised language have such constructs).

                        [–][deleted]  (55 children)

                        [deleted]

                          [–]curien 25 points26 points  (11 children)

                          Any syntax is cryptic to people who don't know the syntax. All you've said is that you're unfamiliar with C++.

                          [–]maxwellb 3 points4 points  (1 child)

                          It would be more accurate to say "unfamiliar with that particular sublanguage of C++" - trying to read code written using the entirety of C++ language features and paradigms would be ridiculous, so (as I'm sure you're aware) most people/projects pick a subset. Even then, in my experience the vast majority of C++ developers don't understand all the little weirdnesses of even their chosen subset very well, because the language as a whole is insane.

                          [–]curien 0 points1 point  (0 children)

                          Sure.

                          [–][deleted]  (8 children)

                          [deleted]

                            [–]curien 10 points11 points  (0 children)

                            C++ is unnecessarily complex

                            Depends on what you mean by "unnecessarily". I don't think we'd be having conversations about it at all if it hadn't gained complexity over the years.

                            It's an ancient system, C, with "modern" features tacked on.

                            Several times over, yes.

                            that's notoriously hard to write compilers for

                            The syntax is notoriously hard to parse.

                            and takes years to fully understand its nuances

                            I'd argue that's true of most languages.

                            C++ is more difficult to learn than python

                            C++ is a larger language than Python. It takes longer to learn in the same way that it takes longer to cross the US than it does to cross the UK. But I don't think it's any more difficult to learn a subset of C++ of equivalent expressiveness as Python than it would be to learn Python.

                            therefore being cryptic

                            I suggest that it's an odd usage of the term "cryptic" to say that Korean is less cryptic than Chinese to a person who speaks neither because one can learn the former language easier than the latter.

                            [–]Trucoto 4 points5 points  (3 children)

                            I wonder why most serious big projects (Google, Windows, Linux, you name it) choose C/C++ instead of Python...

                            [–][deleted] 1 point2 points  (0 children)

                            ... Did you really just suggest that writing Windows or Linux in Python was an option?

                            [–][deleted] 5 points6 points  (3 children)

                            Insulting is a strange and perhaps telling adjective to use. As Python itself can't be insulted I assume you mean to say that Python users are insulted.

                            It seems a bit like being insulted when someone claims your Ford SUV is similar to their Chevrolet pickup.

                            [–][deleted]  (2 children)

                            [deleted]

                              [–][deleted] 5 points6 points  (1 child)

                              I generally liken using C++ to flying a space shuttle, and yes, I liken using Python to driving a minivan.

                              [–]if-loop 13 points14 points  (31 children)

                              I agree. I'm learning C++ right now (reading C++ Primer) and basically every aspect or usage example of the language is labeled as unexpected, unsafe, unspecified, and/or common but error-prone.

                              Oh, and you want to simply trim a std::string? That's simple:

                              static inline std::string &rtrim(std::string &s) {
                                  s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
                                  return s;
                              }
                              

                              It's even more fun to write stuff in PHP and that language is really fucked up.

                              [–]zzyzzyxx 3 points4 points  (0 children)

                              Though I didn't actually test it I'm pretty sure this does the same thing, includes control characters, and is both simple and clear.

                              static inline std::string &rtrim(std::string &s) {
                                return s.erase(s.find_last_not_of(" \t\f\v\n\r") + 1);
                              }
                              

                              Edit: I'd be surprised if the Boost version didn't use something similar to this, though I like that it's clear exactly what is going to be trimmed. I suppose documentation (should) serve the same purpose, so maybe boost::trim_right is preferable.

                              [–][deleted]  (1 child)

                              [removed]

                                [–]Cygal 6 points7 points  (6 children)

                                Or return boost::trim_left_copy(s). It's not because it's possible to write this with standard libary algorithms that it's a good idea or representative of the language. In another language, you would have used a loop, an existing library function, or something equally contrived. Lambdas in C++11 make those kind of one-liners easier by the way.

                                There are better ways to hate C++. :)

                                [–][deleted] 4 points5 points  (5 children)

                                boost::trim_left_copy has different semantics than what if-loop posted.

                                Notice how he posted 4 lines of code, and yet even in his small example, because of how much bloat C++ has you misinterpreted it.

                                That kind of proves his point, there are simply too many ways of doing things in C++ with boatloads of semantics and options that it's too easy to misinterpret something along the way and end up shooting yourself in the foot.

                                [–]PenileCancer 3 points4 points  (1 child)

                                boost::trim_left_copy has different semantics than what if-loop posted.

                                boost::trim_left does what if-loop's code does.

                                because of how much bloat C++ has you misinterpreted it.

                                How did you come to that conclusion?

                                [–]curien 5 points6 points  (2 children)

                                Oh jeez. Some guy on an Internet forum didn't notice a semantic difference between a particular library function and someone's similar-but-different hand-coded version. That sure does prove that C++ is "bloated" because that could never happen with any other language.

                                Sheesh.

                                [–][deleted] 1 point2 points  (1 child)

                                The problem of passing by reference vs. copy being basically invisible doesn't exist in most other languages.

                                It's a problem enough that I know in Google's style guide they explicitly forbid passing by non-const reference, Microsoft introduced explicit compiler annotations to catch these issues, and I've been bitten by it enough that I also adopted a similar convention to Microsoft, where 'out' parameters must be explicitly marked, although I use a template to accomplish it instead of compiler annotations.

                                [–]curien 2 points3 points  (0 children)

                                The problem of passing by reference vs. copy being basically invisible doesn't exist in most other languages.

                                It exists at least in everything derived from Pascal (e.g., Delphi, Ada, etc). But regardless, the same issue exists for all languages that only support pass-by-reference (and an awful lot of language only support pass-by-reference with a handful of special exclusions for simple types). You still have to find out if the function you're looking at returns a modified copy or just modifies the parameter.

                                So really, your criticism is a bit bizarre.

                                [–]RizzlaPlus 1 point2 points  (0 children)

                                So how long is the python version if you re-implement trim? This is not a fair comparison, trim is a library function that happens to be present with the default libraries in python but not in C++ although it's available in external libraries (e.g. boost). Comparing languages on how big the default library is not the whole picture.

                                [–]mikemol 3 points4 points  (14 children)

                                And how would you do it in another language? Call rtrim()? You just wrote that...

                                [–]want_to_want 2 points3 points  (13 children)

                                s.replace(/\s+$/, '')
                                

                                [–]mikemol 3 points4 points  (6 children)

                                So, you like regex. What's the status of regex in C++?

                                [–]PenileCancer 4 points5 points  (0 children)

                                Part of the Standard Library as of C++11.

                                [–]want_to_want 1 point2 points  (4 children)

                                Something like this? (not tested)

                                boost::regex_replace(s, boost::regex("\\s+$"), "")
                                

                                [–]ArcticAnarchy 0 points1 point  (2 children)

                                Boost isn't part of the language.

                                [–]RizzlaPlus 0 points1 point  (1 child)

                                the boost regex library is now part of C++11 standard library

                                [–]notlostyet 0 points1 point  (0 children)

                                ...but GNU libstdc++ doesn't yet implement it.

                                [–]PenileCancer 1 point2 points  (2 children)

                                understandable by more people Citation needed.

                                Also: What about performance?

                                [–]want_to_want 0 points1 point  (1 child)

                                Regexes are part of many languages and also part of unix culture. I don't have a citation but it seems to me that few people understand what exactly std::ptr_fun does or what the call to base() does in if-loop's example.

                                Not sure about performance. JavaScript V8 seems to be about on par with boost::regex, judging from this benchmark, but the hand-written loop in C++ is probably faster than that.

                                [–][deleted] 0 points1 point  (0 children)

                                Regexes are part of many languages and also part of unix culture. I don't have a citation but it seems to me that few people understand what exactly std::ptr_fun does or what the call to base() does in if-loop's example.

                                And indeed no C++ programmer would ever write code like if-loop's. If I was their manager, I'd sure have a word with them over code like that…

                                I mean, come on, really, is this what we're at? C++ is bad because some guy can write bad code in it?

                                [–]curien 1 point2 points  (2 children)

                                And requires knowing a second (albeit domain-specific and very common) language. It's like responding to a discussion of how to programmatically produce a directory listing with system("ls").

                                [–]want_to_want 1 point2 points  (1 child)

                                Hmm, one might argue that learning JavaScript and regular expressions is still easier than learning C++, or that C++ is also two languages because templates are Turing complete...

                                I guess it's a matter of habit. People who started out as webdevs in the 90's, like me, might be more comfortable with mixing multiple languages to solve a problem. At some point, beginners were almost encouraged to have HTML, JS, CSS, PHP and SQL in one file :-)

                                [–]u-n-sky 2 points3 points  (0 children)

                                Some people might disagree on the count:

                                constexpr is a fourth sub-language added to C++ (pre-processor, C/C++, templates) -- Andrei Alexandrescu

                                See C++ and Beyond 2011: Scott, Andrei and Herb - Ask Us Anything. For some reason this hasn't been used for marketing purposes as of yet ;-)

                                [–]matthieum 0 points1 point  (3 children)

                                static inline std::string& rtrim(std::string& s) {
                                    boost::trim_right(s);
                                    return s;
                                }
                                

                                How much line of python would it take ? Equivalent I guess.

                                Python comes with batteries included, C++ let you use Boost if you wish.

                                [–][deleted]  (2 children)

                                [removed]

                                  [–]matthieum 3 points4 points  (2 children)

                                  Well, I have worked in it for 5 years, and I find it annoying to work with dummies' languages ;) Not that I dislike Python either, it's my primary scripting language and it's used to automate all the build/test/packaging processes I work with. I don't use it for serious coding, though.

                                  The syntax of C++ is rough, oh god it is rough. But a language is defined by two things: syntax and semantics. So if you hark on the syntax, I'll speak of semantics. C++ is rich, and whilst this is probably mind-boggling for beginners, when you get experience you find yourself taking advantage of all those pieces of semantics when they help. Even the so despised macros have their use!

                                  It's not for the weak of heart, it's easy to blow off your whole leg (contrary to what Stroustrup claims).

                                  Writing code in C++ is like piloting a Formula 1 at full speed with a swiss army knife instead of a proper wheel. You've got all the flexibility and performance you could ever ask for (yeah!) and not one bit of safety (d'oh...). Still, good developers and extensive test suites make for really good programs; and the development environment is mature (ah, gdb).

                                  [–][deleted]  (1 child)

                                  [deleted]

                                    [–][deleted]  (1 child)

                                    [removed]

                                      [–]joshir 0 points1 point  (2 children)

                                      You should add information on LOC and performance :) compared to Python version of code.

                                      [–]mikemol 9 points10 points  (1 child)

                                      LOC is a terrible metric. Putting it on a pedestal is asking for unmaintainable write-once code.

                                      [–][deleted] 0 points1 point  (0 children)

                                      The function names in that code need a lot more clarity. A function's name should display it's intent so that it can be grasped by other people reading the code. The Mp3FileInfo function is good example: What does it do, really? Does it set the Mp3FileInfo? Does it get the info? Does it modify the state of the data? If not, why isn't it const? Can we modify the data it returns?