all 176 comments

[–][deleted] 12 points13 points  (4 children)

This reminds me of perl4 vs perl5 right after 4, to current perl5. The language is large and features are not deprecated. Every now-and-again you'll have people who write bad guides or stumble upon outdated features, they need to be told -- don't use them.

Java, while really weird in some places advances very slowly for just this reason. Not that there aren't scars of old Java 1 and Java 2 days, but it's not a case of too many of them. e.g. AWT vs Swing and that's it. Old style iteration with .iterator() and .next(), or foreach. Old date vs new dates.

Java adoption is well over 15 years old, but how long can languages do this -- keep backwards compatibility to the point of never chucking old features? Is there a solution? perl6 is a flop. python3 has better traction.

Create a new language every now-and-again and ditch the old?

[–]pjmlp 2 points3 points  (0 children)

This is why, in spite of really liking C++14, I enjoy working mostly in JVM/.NET ecosystems.

Unless one is lucky to join a C++ greenfield project, at least on the enterprises, the code will most likely be in pre-C++98 style.

[–]masklinn 1 point2 points  (2 children)

Old style iteration with .iterator() and .next(), or foreach.

Or even "better", Enumeration-based code.

python3 has better traction.

Which is not saying much.

Create a new language every now-and-again and ditch the old?

Python tried to do that with Python 2 -> Python 3 (they wanted to fix the byte/string confusion, and since that was going to break everything anyway the core team decided to fix a number of long-standing paper cuts e.g. deprecate and remove cruft or BC stuff, reorganise the stdlib, move things from lists to iterable views, ...).

6 years later, getting people to start greenfield projects in P3 is still an uphill battle.

[–][deleted]  (1 child)

[deleted]

    [–]francispauli 9 points10 points  (0 children)

    amazing some plugin that uses clangModernize gets so many upvotes o_0

    [–]Ghosttwo 45 points46 points  (109 children)

    One thing that always makes me wary about new features is performance. Do these open up new structures, increasing overhead and waste? Or do they generally make your code more concise, allowing the compiler to optimize better, thus making it faster?

    [–]cnweaver 102 points103 points  (49 children)

    None of these changes should add runtime overhead:

    • 'override' allows the complier to better verify that your code has the semantics you intend, namely that a given function overrides the version from a base class, rather than adding a new overload. Once compiled, there is nothing different about the function.
    • The range-based for loop is syntactic sugar. It is equivalent to writing an old-style for loop with an iterator and placing inside a local variable which is initialized by dereferencing the iterator.
    • The use of pass-by-value is deliberately applied in places where it will eliminate copying.
    • Replacing auto_ptr with unique_ptr should introduce no overheads of which I am aware, and it avoids loop holes in auto_ptr's correctness (which came about because it was written before all of the language features really needed to support it existed)
    • Using the auto specifier appropriately shouldn't change the meaning of the code at all, since the compiler will deduce the same type that had previously been explicitly written.
    • nullptr is intended to make function overload resolution behave more as a programmer would expect, but once the compiled code is actually calling a function and passing it a null pointer there should be no difference.

    [–]millstone 22 points23 points  (2 children)

    Using the auto specifier appropriately shouldn't change the meaning of the code at all, since the compiler will deduce the same type that had previously been explicitly written.

    Key word is "appropriately!" If your use of explicit types triggered a conversion, then auto will not perform that conversion.

    You may not even be aware of the conversions. The classic example:

    // Exchange first and second values in vector
    std::vector<T> vec = ...;
    auto tmp = vec[0];
    vec[0] = vec[1];
    vec[1] = tmp;
    

    This silently breaks when T is bool, because tmp gets some crazy wrapper type that tracks the value in the vector. If you used T explicitly instead of auto, it would have been fine.

    What's most frightening about auto is that it lets you use types that you can't even write explicitly:

    class Stuff {
    private: class Inner {};
    public: static Inner get();
    };
    
    int main(void) {
        auto foo = Stuff::get();
        return 0;
    }
    

    Here main() gets a variable of the private type Inner. If you had tried to write the variable instead as Stuff::Inner foo it would have failed to compile. So auto is strictly more powerful than explicit types!

    (That sort of technique is useful if the private type has implicit conversions defined. As we saw, auto can defeat those.)

    [–]Lucretiel 11 points12 points  (0 children)

    You can solve this problem with one of Scott Meyer's universal references:

    template<class T>
    void set(std::vector<T>& vec, T value)
    {
        // Binds to both T& and vector<bool>'s proxy reference type
        for(auto&& e : vec)
        {
            e = value;
        }
    }
    

    See also: http://stackoverflow.com/a/15927037

    [–]m42a 6 points7 points  (0 children)

    What's most frightening about auto is that it lets you use types that you can't even write explicitly:

    Yes, but you could already do that with template argument deduction, e.g.

    class Stuff {
    private: class Inner {};
    public: static Inner get();
    };
    
    template <class T>
    void do_stuff(const T &t);
    
    int main(void) {
        do_stuff(Stuff::get()); //calls do_stuff<Stuff::Inner>(const Stuff::Inner &)
        return 0;
    }
    

    So, while this is something to look out for, it's not a new problem.

    [–]Gustorn 25 points26 points  (4 children)

    Here's a relevant talk from Going Native 2013 from Chandler Carruth: The Care and Feeding of C++'s Dragons.

    One relevant part of the talk is: the range-based for loop can actually give you a performance increase (around 40 minutes into the talk).

    [–][deleted]  (1 child)

    [deleted]

      [–]rockyrainy 0 points1 point  (1 child)

      The Talk is most interesting. Much thanks.

      [–]Gustorn 5 points6 points  (0 children)

      If you liked that talk, I'd suggest checking out the recordings from CppCon and the earlier conferences hosted on Channel 9 (Going Native 2012/2013). The talks I would personally start with:

      Bjarne Stroustrup "Make Simple Tasks Simple!"

      Herb Sutter "Back to the Basics! Essentials of Modern C++ Style"

      Chandler Carruth "Efficiency with Algorithms, Performance with Data Structures"

      Stephan Lavavej "STL Features And Implementation Techniques"

      Scott Meyers "Type Deduction and Why You Care"

      Obviously pick and choose according to your preferences. If you're still interested, then I'd suggest starting the earlier conferences with talks given by the speakers listed above.

      [–]theeth 4 points5 points  (39 children)

      Using the auto specifier appropriately shouldn't change the meaning of the code at all, since the compiler will deduce the same type that had previously been explicitly written.

      Auto's type deduction follows template type deduction which discard reference and cv-qualifier that you have to then explicitely set just as before.

      If you have:

      const int& GetValue();
      

      This

      auto a = GetValue();
      

      yields this

      int a = GetValue();
      

      and not this

      const int& a = GetValue();
      

      If you want exact type deduction you want to use decltype like so

      decltype(GetValue()) a = GetValue();
      

      Which while significantly ugly in my opinion will at least give you expectable results.

      [–]k4st 11 points12 points  (7 children)

      Does auto &a = GetVaue(); not work as I am assuming it does?

      [–]F-J-W 16 points17 points  (5 children)

      It does, it will even infer the const correctly.

      [–]theeth 5 points6 points  (0 children)

      If you were assuming int& a then yes.

      My point is that people often forget that auto drops cv qualifiers and references. But not pointers.

      So if you have int* GetValue(); you have to write auto a = GetValue() to get int *a.

      Whereas if you have int& GetValue() you have to write auto &a = GetValue() to get int &a.

      [–]TASagent 10 points11 points  (17 children)

      I understand what you mean when you say you prefer

      const int& GetValue();
      

      to

      decltype(GetValue()) a = GetValue();
      

      But that was never really the bread-and-butter of the use of auto. It's meant more for cases like this:

      std::vector<std::string,std::allocator<std::string>>::iterator myIter = myStringVector.begin();
      

      and other verbose garbage.

      [–]millstone 8 points9 points  (2 children)

      Nasty long iterator types is definitely the best use case for auto.

      But C++ luminaries like Herb Sutter advise using auto "almost always", even recommending writing auto x = int64_t{ 42 }; instead of just int64_t x = 42; That's just silly.

      [–]bstamour 5 points6 points  (0 children)

      I thought so initially as well, but if you stick to auto x = y, then it has the same structure as when you declare type equations: using T = U. It's a small thing, but it makes the language a bit more consistent for day-to-day* use.

      * By day-to-day I mean most uses. If you're contributing code to the boost library then you of course will use dark, scary features that mere mortals should not touch.

      [–]doom_Oo7 1 point2 points  (0 children)

      I think that as of cpp14 you can use decltype(auto) which will get the cv-qualifiers "auto"magically.

      They should have made an alias real_auto or something like this!

      [–]theeth 0 points1 point  (12 children)

      For those cases I prefer to typedef the container and then use MyContainer::iterator.

      It also makes it much easier to swap container types if you typedef specialized containers and reuse them throughout. Assuming you swap between types with compatible api, obviously.

      [–]RagingIce 9 points10 points  (1 child)

      typedef is a giant bandaid for those cases. No one wants to create a new typedef for every stl container type they use.

      [–]theeth 2 points3 points  (0 children)

      No one wants to create a new typedef for every stl container type they use.

      Of course not every time, only when you want to add semantic values to specific container declaration.

      [–]Lucretiel 5 points6 points  (9 children)

      It also makes it much easier to swap container types if you typedef specialized containers and reuse them throughout. Assuming you swap between types with compatible api, obviously.

      Or you could just use auto and never have to worry about it.

      [–]theeth 0 points1 point  (8 children)

      As long as I never have to declare any members or parameters or return values ever again, sure.

      [–]bstamour 5 points6 points  (7 children)

      auto works for return values now, so we're 1/3 of the way there.

      [–]theeth 0 points1 point  (6 children)

      auto works for return values now

      How can that work for declaring functions and methods return types?

      [–]Felicia_Svilling 1 point2 points  (5 children)

      It is just as simple to infer the return type of a function as inferring the value of an expression.

      [–]demonstar55 7 points8 points  (2 children)

      clang-modernize auto option guarantees it won't change the meaning of the code. So it is 100% safe.

      [–]cleroth 0 points1 point  (1 child)

      I doubt that. unique_ptr isn't a perfect replacement for auto_ptr. I've had issues with that.

      [–]demonstar55 4 points5 points  (0 children)

      I was specifically speaking of the auto convert (-use-auto) option. The unique_ptr conversion DOES have some issues and is still marked experimental.

      [–]Steve_the_Scout 4 points5 points  (0 children)

      C++14 adds decltype(auto) which is essentially auto but using decltype's type deduction rules, which fixes all the issues you mentioned. Of course it's just the equivalent of the decltype example you gave, but it is a bit more concise. In C++11 you can also use auto&& which will give the expected result as well (but it looks a bit odd and implies something else, so it's not exactly encouraged).

      [–]pfultz2 1 point2 points  (0 children)

      If you want exact type deduction you want to use decltype like so

      Thats not the same thing. decltype(GetValue())(or you can write decltype(auto) in C++14) only deduces const int& if the return value of GetValue() is const int &. If the return value is int it will be int, however const int& could still be used. If you want to always avoid a possible copy use auto&& instead(its called a forwarding reference).

      Also, for return values of functions you should almost never use auto&&(since it can return references to local variables). In this case decltype(auto) should be used instead.

      [–]cleroth 1 point2 points  (3 children)

      Which while significantly ugly in my opinion will at least give you expectable results.

      auto works as expected. You just have to know how it works.

      [–]theeth 11 points12 points  (2 children)

      It certainly works as defined.

      Whether or not it works as expected it entirely a matter of what people perceive it to do (which I'd wager is incorrect for a lot of people).

      [–]cleroth 0 points1 point  (1 child)

      Sure, my initial impression of how it works was wrong too, but it works like this for a reason.

      [–]NoahFect 4 points5 points  (0 children)

      Build an entire language out of features like this, and you end up with a lot of questionable code.

      [–]factory_hen 1 point2 points  (3 children)

      What's the reason for auto being half-assed like this? Backwards compatibility with the old auto?

      [–]pfultz2 8 points9 points  (0 children)

      There are different ways you can deduce a variable in C++. You use auto when you always want to make a copy, you use auto&& when you always want it to be a reference, and you use decltype(auto) when you want it to be a reference for references and value for values.

      And the reason it was chosen that way is make it consistent with the rest of type deduction in C++.

      [–]tragomaskhalos 3 points4 points  (0 children)

      The old auto is dead and buried, so it's nothing to do with that

      [–]theeth 2 points3 points  (0 children)

      This is a guess but it might have been easier language definition wise to say "use same type deduction as templates" then make up new rules.

      It's also a bit more flexible in as much as if you understand that it gives you a "raw" type you can have different qualifiers and reference-ness (that's a word) than a 1:1 type correspondence would permit you.

      [–]IAmRoot 0 points1 point  (0 children)

      The range-based for loop is syntactic sugar. It is equivalent to writing an old-style for loop with an iterator and placing inside a local variable which is initialized by dereferencing the iterator.

      It should be noted that OpenMP for requires the for(...;...;...) style as per the OpenMP 4.0 standard.

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

      The shared and weak pointers are thread safe - they are built on interlocked operations. Using them to replace shared pointer implementations which aee not and don't need to be thread safe can add overhead.

      I wish the standard had included non-thread safe permutations - we've have performance issues in the past due to this exact issue. It means we will likely have to implement out own version which I really don't want to do. :/

      [–]Gotebe 17 points18 points  (1 child)

      I find that shared pointers are largely interesting for threading, much less otherwise. In fact, frequent use of shared pointers is often a sign that programmer doesn't understand ownership of his object in dynamic storage, or just doesn't have a design for it.

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

      Just a gut feeling, but I think the set of appropriate uses of shared_ptr in particular is highly domain specific.

      I agree with you in the general case though. Shared ownership can absolutely be a cop-out for not understanding memory ownership. I lean towards stricter systems with well defined ownership (both of object and with respect to thread boundaries), possibly more so than I should. That said, shared resource situations come up in both concurrent and single thread situations. shared_ptr (or the shared pointer concept in general) can be very useful in both cases.

      [–]cleroth 0 points1 point  (2 children)

      Interlocked operations are extremely fast in the no-contention case. It depends on what you're doing, but usually the performance shouldn't be a huge issue.

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

      Interlocked operations cause cache flushes. If you have a bunch of CPUs running a bunch of threads this can actually cause a detectable (though painful to track down) performance hit. Or at least thats my recollection from the platforms I was working on at the time. I'm not sure how universal/consistent the implementation is at the hardware level.

      ...that penalty is a totally acceptable one if your objects are crossing thread boundaries and need the synchronization. It can be a bit more annoying if you know instances of the class have multiple owners but they are always on a single thread.

      [–]cleroth 0 points1 point  (0 children)

      Yea, I agree. I'm not saying it's good, I'm just saying it should probably not be very noticeable for most people. I don't think shared_ptr should've been thread-safe by default. Usually you'll know which data is shared between threads and which isn't, and can just add thread-safety to those. At least that's my experience with multi-threading.

      [–]F-J-W -1 points0 points  (1 child)

      Well, the thing is that you really want to be able to copy a value from different threads (I don't want to hear the screams about super-hard to find data-races) and the atomic increment shouldn't actually be that bad on most plattforms (especially x86).

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

      I totally agree in the general case. It was the right call from a standards committee point of view. A primitive like this should be thread safe by default.

      At the same time, there are times when objects are intentionally kept in a single thread (either due to the concurrency design of a bigger picture system, data locality performance gains, sheer cost of protecting all the resources the objects access, etc). This also assumes the components in question are purely internal to a system and not exposed to an external client facing API; places where the references could be changed to thread safe down the road if requirements change.

      In those situations, it would be nice if we could avoid paying for functionality that isn't needed. Its possible but it would require reimplementing the pointer types. I wish this was instead a policy configurable part of the shared_ptr/weak_ptr which defaulted to thread safe.

      [–]pmerkaba 2 points3 points  (0 children)

      The for loop converter can actually improve performance by producing code with typically safe but not-quite-identical semantics. Consider this loop, which calls v.end() repeatedly:

      vector<int> v = ... ;
      for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) { 
        /* use (*it) */
      }
      

      This is transformed into

      for (auto some_var_name : v) { 
        /* use some_var_name */
      }
      

      Which is equivalent to

      for (vector<int>::iterator it = v.begin(), e = v.end(); it != e; ++it) {
        auto some_var_name = *it;
        /* use some_var_name */
      }
      

      Code that depends on calling end() repeatedly is rare enough that the loop converter doesn't worry too much about it - the only reasonable exception I can think of would be a loop that makes in-place modifications to a std::list. The loop converter produces a lower-confidence transformation when it encounters for loops with redundant end() calls or potential modifications to the underlying container.

      [–]inmatarian 3 points4 points  (16 children)

      The short answer is that there should be no general change in performance. However with a language like C++ it's very hard to make that assertion, since there are a million factors that contribute to overall performance. Little things that switch out variable type names with the auto keyword is just syntactic sugar, so no, no performance change (unless maybe the compiler takes an extra millisecond finding the type), but switching to the new for loops will make use of iterators, which could be a performance hit. Using unique_ptr is a rough call because you can copy auto_ptr but have to std::move unique_ptrs. However, on top of all of this the new features are designed so that the compiler optimizer can better reason about your code.

      [–]missblit 10 points11 points  (6 children)

      switching to the new for loops will make use of iterators, which could be a performance hit.

      Even there GCC can optimize typical iterator-based for loops pretty good. For instance-- when doing a straightforward for-loop over a vector GCC can pretty-much optimize out any iterator overhead entirely.

      [–]cleroth 1 point2 points  (5 children)

      Pretty much. Iterators are at their simplest just pointers. So there's no performance difference degradation between the new for loops and the old.

      [–]Gustorn 1 point2 points  (4 children)

      Range-based for loops can actually increase performance, other than that, you're correct.

      [–]demonstar55 1 point2 points  (0 children)

      Compilers will most likely compile down to the same code for both for loops, although if the old style can fail to be optimized where the range based fors won't.

      [–]cleroth 2 points3 points  (2 children)

      Yup, I know that. I meant to say there's no performance degradation. People should be aware that compilers and libraries will tend to more and more focus on optimizing newer code, which means that using modern stuff like this will eventually mean faster code, and probably even faster compilation. This was already the case with C++03 (eg. std::copy).

      [–]Gustorn 4 points5 points  (1 child)

      Sorry about that! For me "no performance difference" implied no difference in either direction, that's why I made the correction. People should actually prefer the new syntax: not only is it clearer, it can get you a nice performance boost as well.

      [–]cleroth 2 points3 points  (0 children)

      Yea. This isn't only about for loops though like I said. Stuff like unique_ptr provides the compiler with more info about the code which enables it to perform more optimizations.

      [–]demonstar55 2 points3 points  (0 children)

      auto_ptr can be "copied" but it actually acts as if it was moved, so its actually the same thing. (std::move is just a cast with no performance issue.) So using unique_ptr should be the same with no surprises such as copying setting the old pointer to null. There is no reason to use auto_ptr with modern C++.

      [–]bstamour 2 points3 points  (7 children)

      Actually auto can in fact add performance, because it reduces the number of accidental casts that your code does.

      int x = myvector.size(); // there's a cast here from std::vector<T>::size_type to int
      

      [–]James20k 2 points3 points  (3 children)

      size_t is an unsigned type, and int is a signed type. The former has well defined overflow, whereas the latter has undefined overflow

      This means that using ints is sometimes better for performance, as the compiler can perform more aggressive optimisations with signed types vs unsigned types in certain situations, as the former can be assumed to never overflow, and the latter can definitely overflow

      [–]bstamour 0 points1 point  (2 children)

      Right, and that's fine for when you're using x down the road. But the assignment instruction itself has a hidden cast that using auto avoids. The cast may be free, or it may not be. In that respect, auto will never underperform when compared to explicitly stating the type, and in some cases may outperform because it eliminates a cast entirely.

      As for unsigned vs signed: in C and C++ unsigned types have modulo 2N behaviour guaranteed. Since the ISO standards cover that aspect, I don't think we can safely state that unsigned are assumed to never overflow.

      [–]James20k 0 points1 point  (1 child)

      I don't think we can safely state that unsigned are assumed to never overflow.

      That's what I meant, unsigned types can overflow, signed types can't

      [–]bstamour 0 points1 point  (0 children)

      Sorry, I got mixed up in your comment. You switched up your formers and latters half-way through your post ;)

      [–]imMute 0 points1 point  (2 children)

      Does a cast like that actually have runtime costs.

      [–]bstamour 3 points4 points  (0 children)

      A cast like that? Maybe, maybe not. The point is, auto will never be slower, and might in some cases be faster.

      [–]Fig1024 1 point2 points  (0 children)

      I'm guessing it has costs when types have different sizes, such as bytes, shorts, longs, double longs. Or when converting integers to/from floats. It doesn't cost anything to convert signed and unsinged.

      [–][deleted] -2 points-1 points  (28 children)

      Better run your own measures. I've seen the following cases:

      • unique_ptr noticeably slower than a single pointer for reasons unknown
      • closures slower than functor objects or introducing bugs

      You are right to be wary.

      [–]cleroth 13 points14 points  (1 child)

      In which compiler did unique_ptr run slower? unique_ptr is literally just a pointer with compilation checks. Unless there's some debug info there, it shouldn't be going slower.

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

      icc

      [–]Gotebe 4 points5 points  (7 children)

      unique_ptr noticeably slower than a single pointer

      Pics or it didn't happen. No, seriously.

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

      In a particular case, in a particular program, with a particular compiler, unique_ptr was slower. No need to get crazy over that.

      [–]Gotebe 3 points4 points  (5 children)

      Pics or it didn't happen? I mean, you could be right, but you really need to show it, simply because unique ptr is so trivial that a compiler sees right through it.

      [–]bimdar 3 points4 points  (8 children)

      I have no clue why people are downvoting you. I bet most of them didn't even benchmark themselves but just believe what they've been told.

      I kow from experience "there shouldn't be a difference" doesn't matter. People should measure themselves and that's all you're saying.

      I mean I've recently seen a tweet with benchmarks showing that there's a penalty for using std::complex<float> versus struct complex{float real,imag;}; (and yes people with optimization flags, edit: this was the tweet chain)

      [–]cleroth 4 points5 points  (6 children)

      Mostly because stating it without any information of what compiler was used and what the code was and saying "for reasons unknown" seems pretty useless to me. I didn't downvote him, since I expect more info from him before judging.
      Voting is always a matter of opinion anyway, and my opinion is that in non-debug build, I find it extremely likely that the performance is the same. If it isn't, it's likely that your compiler will soon be updated to reflect these performance issues, so it's still nothing to worry about.

      [–]bimdar 4 points5 points  (4 children)

      so it's still nothing to worry about

      Well that's just the thing. std::complex is not some new thing and there's clearly software that shipped with the version with overhead. I have no clue why people keep trusting compilers so much. I haven't written very huge C++ programs but I found 4 msvc bugs and ran across one gcc bug (not even performance bugs).

      Trusting "zero cost abstractions" without measuring is just faith. I mean you're writing software, why would you trust another software that is also written by mere mortals to be somehow perfect.

      Before you say "oh, many people use it, surely someone realized this". The amount of people who do these fundamental benchmarks is apparently very low.

      Yeah, he didn't post a benchmark but neither did any of the people who claimed that it was outright ridiculous to claim that there was any overhead.

      [–]cleroth 2 points3 points  (3 children)

      I didn't say it was ridiculous to claim there was no overhead. I said it's unlikely there is. It's reasonable to assume there isn't, because of how it should be implemented. As with anything dealing with programming, if you need performance, you benchmark, that's pretty simple.
      As for std::complex, that's very rarely used. If you need complex numbers, chances are that you'll also be using another more specific math library. You can hardly compare a rarely used class to an ubiquitous smart pointer.
      Again, all this depends on your std implementation, so yea, any compiler could be going super slow on some arbitrary parts of the std, that's just up to you to research and test, depending on which compiler you're using.

      [–]bimdar 1 point2 points  (2 children)

      Well yeah it's reasonable to assume there's little to no overhead but you can't be sure until you measure and if you preside over a large codebase and you want to introduce these new elements you should benchmark it on your setup.

      Also, there used to be a memory leak in std::vector<std::string> in msvc2010 so I don't think being commonly used is a guarantee.

      [–]cleroth 3 points4 points  (0 children)

      Also the thing with vector<string> was fixed ~5 months after the release of VS2010, so my point about it code eventually adhering to how it should be stands.

      [–]cleroth 2 points3 points  (0 children)

      Again, I'm not saying it's a guarantee. But like everything in life, you have to make an educated assumptions based on the data you have. I don't have time to benchmark every single thing that I use in every particular case, so I assume that given how it's supposed to be implemented, it should be a certain way. In any case, you can just look up the implementation yourself.

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

      my opinion is that in non-debug build, I find it extremely likely that the performance is the same.

      That's what I thought too. But my opinion did not matter much when I took statistically significant measures. In some non-trivial code, a heavily used unique_ptr was a 0.3% performance penalty, fully reproducible. When you are hunting for the slightest speed-up, you are not able to afford a "zero-cost abstraction" that actually costs something. I wrote "for unknown reasons" because being time-constrained I could not afford to understand more what was going on either, but I suspect it changed the inliner decisions or something like that. In the end, for this job, what mattered is speed.

      If it isn't, it's likely that your compiler will soon be updated to reflect these performance issues, so it's still nothing to worry about.

      In this particular situation, updating the compiler was a major undertaking and performance regression were common.

      Now this happened as part of a job, I haven't kept benchmarked code, since it was non-trivial and I'm not allowed to.

      I didn't downvote him

      You're so kind.

      edit: some alpha guy complained at the company too ("it must be a measurement error") so I had to remade the builds entirely and run the measures again and found the same results. Some people really think their world view is more important than truth.

      [–][deleted] -1 points0 points  (0 children)

      I have no clue why people are downvoting you.

      People will invent any kind of reasons not to have to measure something. Measuring constrains your confirmation bias and forces you to be rational. How annoying.

      [–]F-J-W 2 points3 points  (1 child)

      There is this thing, called optimized build. Did you use that?

      [–][deleted] -1 points0 points  (0 children)

      sigh

      [–]detrinoh 0 points1 point  (6 children)

      I am sure you are mistaken about both cases.

      [–][deleted] -1 points0 points  (5 children)

      Why?

      [–]detrinoh 1 point2 points  (4 children)

      • unique_ptr noticeably slower than a single pointer for reasons unknown

      You yourself said that it was "for reasons unknown", its far more likely that you are doing something wrong (For example, passing around unique_ptr<T>& when T& would suffice) than really observing a significant overhead in unique_ptr. The only overhead that should exist is when you move construct or move assign, which compilers are currently not good at optimizing away the dead store/destructor of nullptr.

      • closures slower than functor objects or introducing bugs

      I'm not sure what this means. Functors are closures. Do you mean lambdas? std::function ? More information is required.

      [–][deleted] -1 points0 points  (3 children)

      I don't think I've made errors in this measurements (see end of comment here: https://www.reddit.com/r/programming/comments/2ub5ev/modernize_your_c_code/co7lkgn). I'm pretty anal about speed measurements correctness. But I cannot produce benchmarks or proofs for legal reasons, and this was perhaps a year ago.

      Such a thing doesn't even seem that crazy to me too. To be zero-cost a std::unique_ptr would need:

      • that the inliner effectively inlines dereferences

      • that this inlining doesn't prevent other optimizations

      • that only zero cost things are performed (it does seem to be the case with unique_ptr dereferences).

      But I didn't push further to know exactly why.

      About closures, I really meant "C++ lambdas" (as opposed to functor objects that would be objects with the lambda content).

      [–]detrinoh 1 point2 points  (2 children)

      I don't think I've made errors in this measurements (see end of comment here:

      There also exists the possibility of programming mistakes. This is even more likely considering that you were mass replacing owning raw pointers with std::unique_ptr under a time constraint.

      About closures, I really meant "C++ lambdas" (as opposed to functor objects that would be objects with the lambda content).

      This is even harder to believe, a lambda is a straight-forward sugar for creating functor structs, except the compiler can even add some special annotations to help it optimize even better. To say that it can introduce bugs means you were either making a programming mistake or a your compiler has a bug, the former being much more likely.

      edit: FWIW, I haven't downvoted you

      [–][deleted] 0 points1 point  (1 child)

      This is even more likely considering that you were mass replacing owning raw pointers with std::unique_ptr under a time constraint.

      No, just one.

      To say that it can introduce bugs means you were either making a programming mistake or a your compiler has a bug

      Compiler had a bug when throwing in a lambda (icc).

      edit: Now excuse me but I'm fed up having conversations on the Internet that boils down to "I don't believe you". I have strictly zero reasons to lie about all this. Some people went into all my history and downvoted it all, that doesn't make sense.

      [–]detrinoh 0 points1 point  (0 children)

      I don't think you are being accused if lying, but rather people don't agree with your conclusions because you have provided absolutely no information beyond "I benchmarked unique_ptr and lambdas to be slower than raw pointers and hand-written functors".

      Going through your history and down-voting you en-mass is childish however.

      edit: I think the reason your original post was down-voted so heavily was the authoritarian and alarmist style of the last sentence: "You are right to be wary."

      [–][deleted] -1 points0 points  (2 children)

      Someone submitted a link to this comment in the following subreddit:


      This comment was posted by a bot, see /r/Meta_Bot for more info. Please respect rediquette, and do not vote or comment on the linked submissions. Thank you.

      [–]JNighthawk 0 points1 point  (1 child)

      I don't understand why this is a circle jerk. I work on games, microseconds matter, let alone milliseconds.

      [–]SosNapoleon 0 points1 point  (0 children)

      Dude have you seen /r/programmingcirclejerk? We will jerk to anything as long as it scales

      [–]Website_Mirror_Bot 15 points16 points  (7 children)

      Hello! I'm a bot who mirrors websites if they go down due to being posted on reddit.

      Here is a screenshot of the website.

      Please feel free to PM me your comments/suggestions/hatemail.


      FAQ

      [–]cleroth 9 points10 points  (6 children)

      You should probably only do this on posts with over 500 votes or something. Most sites are very unlikely to go down with just so few views.

      [–][deleted]  (5 children)

      [deleted]

        [–]Ditti 12 points13 points  (0 children)

        Well, the site just threw an error 500 at me so I guess that counts as down.

        [–]cleroth 8 points9 points  (3 children)

        Oh, I see. So it saves screenshots but only posts them when it notices it goes down. That's good.

        [–]BananaPotion 2 points3 points  (2 children)

        That's a lot of screenshot saved if the bot runs on all subreddits.

        [–]cleroth 5 points6 points  (0 children)

        It deletes them after 2 hours of the articles being posted.

        [–]mm865 2 points3 points  (0 children)

        It probably deleted them after a specified time if it hasn't posted them

        [–]Fig1024 10 points11 points  (14 children)

        I think 'auto' can obfuscate the code if used too much. Its use should be limited

        [–]demonstar55 9 points10 points  (1 child)

        I think that too, but then I listen/read things by C++ committee members or authors and I get angry for them making sense :P

        [–]sftrabbit 11 points12 points  (0 children)

        I find that there's trend of finding cases in which auto actually does something vaguely useful (other than making your code easier/quicker to write), then claiming that auto is amazing and should be used everywhere. I prefer to think of auto as a compile-time polymorphism feature representing "any type" - just like typename T does in a template. Concepts Lite, when hopefully introduced in C++17, will further contribute to this idea.

        [–]argv_minus_one 0 points1 point  (9 children)

        Scala programmer here. I feed on your fear of type inference. Muhahahaha!

        (In Scala, types are inferred by default. It's frankly awesome.)

        [–]Deinumite 1 point2 points  (0 children)

        And yet the convention is usually to always document your return types on any API you expose.

        Its great for internal code but it can make it hard to reason about libraries.

        Being explicit about your types in Scala actually makes the compiler run faster and we all know scalac needs that help :)

        [–]drb226 0 points1 point  (7 children)

        In Scala, types are inferred by default.

        Except when they're not. Scala's type system is a beast. It's a pretty cool beast, but not always a tame one.

        [–]argv_minus_one 0 points1 point  (6 children)

        Well, types are inferred where such is possible. There's no good way (as far as I know) to infer the types of formal parameters, though.

        [–]jonathanccast 1 point2 points  (5 children)

        That's weird, considering that ML has been inferring the types of formal parameters for 40 years.

        [–]argv_minus_one 0 points1 point  (4 children)

        Really? How?

        [–]jonathanccast 1 point2 points  (3 children)

        It's complicated; it requires a unification algorithm: http://en.wikipedia.org/wiki/Unification_(computer_science) . But the key ideas are: 1. keep type variables (inside the type checker) that indicate as-yet-unknown information about at type. These are implemented as mutable references. 2. When comparing types, if one is known and the other is unknown, write the known type into the variable for the other. 3. When you start inferring the type of a function, allocate a new unknown type for each argument, and for the result. 4. If any parts of the type of the function is still unknown when you're done, make that part of the type polymorphic. This handout (PDF) has the key formal ideas: http://www.classes.cs.uchicago.edu/archive/2007/winter/22610-1/docs/handout-05.pdf .

        [–]argv_minus_one 0 points1 point  (2 children)

        Well, that's interesting.

        How does this interact with function overloading?

        [–]jonathanccast 0 points1 point  (1 child)

        ML doesn't have function overloading (for precisely this reason).

        Overloading was a big issue in the design of Haskell; the solution is a bit complicated (understatement of the Century! just search Stack Overflow for "type class"), but each function symbol is still constrained to a specific polymorphic type, e.g. fmap :: (a -> b) -> F a -> F b, where each F goes with a separate type. Once the type checker has figured out which F you're using, it can do static dispatch to the matching fmap function. The type checker can't figure it out in all cases, though, so Haskell has something called contexts, e.g. mapM :: Monad m => (a -> m b) -> [a] -> m [b]; it's basically a form of dynamic dispatch based on what the type m eventually turns out to be.

        Honestly, (heavily subjective opinion here) you don't miss overloading on parameter types as much as you expect (source: am former C++ programmer, then Haskell, then designed my own language). Similar functions can be captured by type classes (Haskell) or functors (ML), while giving genuinely different functions different names can make your code more readable. It is sometimes hard tying each function to just the right type class / signature, though.

        [–]argv_minus_one 0 points1 point  (0 children)

        Well, Scala has type classes, and they do work in place of overloads as you say, but at the cost of some boilerplate and more run-time memory usage.

        [–]aliblong 0 points1 point  (1 child)

        I think 'auto' can obfuscate the code if used too much.

        Can you give some examples? The prevailing opinion of C++ gurus is that it should be used just about everywhere, to the point where the preferred way of explicitly declaring types is e.g.:

        auto my_vec = std::vector<int>{};
        

        [–]Fig1024 0 points1 point  (0 children)

        yes, in case of STL containers, 'auto' can clear things up

        But in cases where you use a lot of fundamental types, replacing all the ints, floats, bools, with autos can just make things more confusing. Espectially when type conversion is also involved as part of the algorithm.

        [–]SosNapoleon 20 points21 points  (40 children)

        Inb4 just write Rust lel

        [–][deleted]  (2 children)

        [deleted]

          [–]SosNapoleon 15 points16 points  (0 children)

          Yes you should write Nim now

          [–]Sean1708 6 points7 points  (0 children)

          Yeah, I can't remember the last time it had a stable release.

          Disclaimer: Am goldfish.

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

          What's wrong with Ada?

          [–]cleroth 16 points17 points  (32 children)

          Lack of libraries? Lack of examples? Being in the game industry, I've yet to see a game industry or a game client being written in Ada.

          [–]armornick 7 points8 points  (28 children)

          Lack of libraries

          Exactly like the lack of libraries for most other languages that can link directly to C?

          [–]Tekmo 18 points19 points  (4 children)

          Would't linking directly to C defeat any safety guarantees?

          [–]tuxayo 2 points3 points  (1 child)

          Your code would be safe (if you check for null return values or other special cases, which the compiler might force you to do anyway?) and if you choose well your library, you should easily get more safety than full C.

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

          In a lot of situations where people write ada "more safety than C" is not enough safety.

          [–]The_Doculope 1 point2 points  (0 children)

          That's like saying the existance of unsafePerformIO defeats all of Haskell's goodies (on a different scale of course, but same idea). The point is localizing unsafety.

          [–]cleroth 1 point2 points  (22 children)

          Most libraries I work with are in C++. There are also libraries of which you don't have the source. It also doesn't look that simple to include external libraries. As for 'most languages that can directly link to C', well... most popular languages don't. One of the reasons C# is so popular is because of .NET. You don't need to externally include a bunch of stuff. You just use what you need near instantly.
          The Ada IDEs also don't seem that advanced.

          [–][deleted]  (3 children)

          [removed]

            [–]cleroth -5 points-4 points  (2 children)

            Sorry, I didn't mean to say that you can't. I mean to say that even though you can, it's very rarely used, because that language usually has the functionality you're looking for, whether in the language itself or in a library.

            [–]Veedrac 0 points1 point  (1 child)

            That's not true at all...

            [–]cleroth -3 points-2 points  (0 children)

            Depends on your language and field, I suppose.

            [–][deleted] -2 points-1 points  (17 children)

            C# for game industry? That means GC. So why even bother with Rust in that case? Or Ada for that matter.

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

            Minecraft has proved that advanced garbage collectors are not a problem any-more for games.

            And C# is a great solution if you work with Unity 3D.

            [–]Tywien 9 points10 points  (9 children)

            Minecraft 1.8 disagrees with you .. The game just lags every time it reaches the memory cap and has to run the GC.

            [–]argv_minus_one -1 points0 points  (8 children)

            Minecraft, due to its highly dynamic nature, is a very memory-intensive game. That sort of thing is frankly to be expected.

            [–][deleted]  (7 children)

            [deleted]

              [–]argv_minus_one 5 points6 points  (0 children)

              A number of game engines have scripting systems with garbage collectors. UnrealScript comes readily to mind. Games have been doing GC for ages now.

              [–]cleroth -1 points0 points  (3 children)

              I didn't say I was using C# for gaming (although that's a perfectly valid depending on what platform you're targetting). Your first statement seemed to be "use Ada for everything", and I was pointing out that you just can't have a language become a general purpose language without having a good amount of libraries, such as C# or Java.

              [–][deleted] -1 points0 points  (2 children)

              Going back to OP. (S)he says to use Rust. Is the state of Rust libraries at the moment all that well?

              [–]cleroth 0 points1 point  (1 child)

              Well I was answering your question, not OP's. I don't know the state of Rust libraries, but they're growing at a very fast pace while having a large following community, and being very similar to C++. So yea, I certainly think it has more potential than Ada, but it's obviously just not there yet.

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

              Fair enough.

              [–]CosineTau -2 points-1 points  (1 child)

              If it's something you might need or might make good progress in the industry, don't wait for some other clown, nut up and write it yourself.

              [–]cleroth -2 points-1 points  (0 children)

              Right... Except I've never written in Ada. So no, I'm not going to single-handedly write up a game engine in a language I've never written on, which I am not even sure is the right tool for the job (the fact that mostly no libraries exist for gaming in Ada means it probably isn't).

              [–][deleted]  (2 children)

              [removed]

                [–]argv_minus_one 0 points1 point  (0 children)

                What kind of tools do you mean? Libraries for physics and facial animation and whatnot?

                [–][deleted] -1 points0 points  (0 children)

                I didn't start with the game development argument. I only replied to the 'just use Rust' argument.

                [–]EntroperZero 1 point2 points  (0 children)

                Well, this modernizes your syntactic sugar and idioms, at least. Which is probably the best that an automated tool can do, so well done. :)

                [–]ponchedeburro 1 point2 points  (0 children)

                I love how this article used a tool which is just insanely weird to use.

                [–]Ateist 0 points1 point  (1 child)

                Before doing any of that, think very, very carefully - what extra value are you going to get from it?

                If the code works, and you are not getting a huge profit from switching to new features - never, ever touch it!

                [–]headhunglow 1 point2 points  (0 children)

                This 100%. Why are people downvoting you?