Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

In addition to the new trendy languages, I suggest learning modern C++. Java is a good set if training wheels if you want to learn OO patterns.

C++’s template system is turing complete, which means its type language is too. That lets you move the vast majority of java runtime type errors to compile time.

Moving from Java to modern C++ is analogous to moving from spaghetti Python code to well-structured Java in my book.

Also, C++ is extremely well established.



> moving from spaghetti Python code to well-structured Java

Python code quality is more a consequence of its beginner-friendliness (and subsequent prevalence of inexperienced programmers) than of the language itself. Similarly, the range of Java code I've seen goes from spaghetti within spaghetti (i.e. lack of structuring at both code and class/package level) over to extremely well-structured code


Not to forget over-structured code, where even the simplest internal helper class has an interface and a factory. So the sweet spot seems to be somewhere in the middle :)


>C++’s template system is turing complete, which means its type language is too. That lets you move the vast majority of java runtime type errors to compile time.

Never heard C++'s templates turing completeness described as some kind of advantage.


Go look at json for modern C++.

You write declarative specifications of your data types and it gets the C++ compiler to generate json serializers and deserializers.

(Note that C++ templates are a statically typed, lazily evaluates, pure functional language, so many of the advantages of Haskell, etc. can be shoe-horned into C++ types).


So, I'm curious, what is it about modern C++ that you think is better than Java?

Because I know (old) C++, some of the modern stuff and a bit of Java, I don't know the full history.


I'm not familiar with modern Java, but modern C++ has been advancing so fast that people are calling for a cool-down. The counter for "C++ is already too complex" is that many of the new complexities are being put in place as simpler replacements for common boilerplate. For example:

Pointers, new, delete, malloc, free are all still there. But, you really don't need them any more. You can write large-scale programs using only objects, containers, iterators and unique_ptr (and shared_ptr if you can't clean up your design enough to avoid it). You basically get full memory management without garbage collection and while retaining RAII destructor capability.

"Optionally define this function based on these class properties. Else, define this alternative implementation" used to require a significant about of template boilerplate to pull off SFINAE. That has been cut down dramatically by if constexpr (). constexpr in general has been expanding to the point that someone wrote a compile-time ray tracer just to see if he could. Also, the oddly-named void_t makes it easy for if constexpr to "define this code if this statement will compile, else do this alternative". Between if constexpr, modules and a few more extensions (source_file/source_line?) there is a goal of eliminating 90%+ of the need for the preprocessor.

Functions with auto parameters, auto return types and containing some if constexpr can effectively be powerful templates without any <>.

Lambdas are obviously a shorthand for "functor" style class definitions. And, they can (auto)->auto.

range based for(auto &x:container) is much simpler than for(int i=0; i<n; i++) even though you are so familiar with for(;;) that you barely see it any more. But, even better, the standard algorithms spell out intent much better than a generic loop in 90% of cases.

Coroutines are already implemented in multiple compilers and are being standardized currently.

Variadic templates, parameter packs and tuples make wrapping arbitrary function signatures fast and easy.

nodiscard return types eliminate a whole class of errors. optional<T> return types are nice. The upcoming "zero-overhead deterministic exceptions" and expected<T,Error> are big improvements to error handling.


"You basically get full memory management without garbage collection"

No, you don't. Reference counting is not equivalent to garbage collection.

Edit: Garbage collection has no problem collecting circular references if they are out of scope. Not so much for reference counted pointers. You will start leaking memory accidentally if you are not carefull - i.e. reference counting is not "automatic full memory management" - it will delete stuff once it's reference count goes to zero but it you will have to be careful not to come up with circular references that will leak.


But you don’t get full memory management with garbage collection either.

Take MVC GUI programming in Java, which I mention because the thread is about Java, and considerable effort was made by the core Java team at Sun to make this work well.

If you use swing or awt and you call “longLivedThing.addEventListener(shortLivedThing)“ then you’ve written a memory leak.

The two options: (1) create a directed a cyclic graph encoding desired object lifetimes, and make sure references obey it, or (2) use weak references to manually break the cycles.

(1) is much easier in C++ because you can use rich reference types to get the compiler to automatically audit the lion’s share of your codebase. I don’t know of anyone that actually does (2) in Java because it makes it extremely difficult to reason about the actual runtime behavior of the program.


If you can manage to not use shared_ptr (and, as the author says, you should try to) then you are not reference counting and you don't have circular references. As with Rust, the very few places where you may need such constructs can be carefully isolated, the other 99% of your code is "basically" safe from dangling pointers and memory leaks.


Don't write circular references. That's shit design.


How in the world would you get full memory management without garbage collection in c++?


The author above is referring to reference counting. And no, there is nothing to capture circular refernce.


Yes, I thought about that, but ARC is a form of garbage collection, so you can’t have automatic memory management in C++ without garbage collection, unless I’m missing something quite fundamental...


OK. ok. "full" was an overstatement. You got me.

The point is that you can write large programs without ever calling delete or free() and still not leak memory. True, it's not completely general nor impossible to screw up. You still need to be aware of issues like circular references. And, using shared_ptr would make lifetime as ambiguous as a Java reference. But, if you keep everything a DAG of containers and unique_ptrs, not only will your memory thank you, your coworkers will too.


Thanks, that make a lot of sense. I'm favouriting this comment.


C++ supports more complex inheritance models.

Operator overloading

The const keyword

C++ can guarantee stack allocations

C++ has faster boot times

C++ templates are more powerful than Java generics

C++ latency is better, which is important for some applications such as audio

C++ can manage external resources better using RAII

Not saying C++ is better in all cases, but it does bring things to the table that Java does not.


OP already said:

> That lets you move the vast majority of java runtime type errors to compile time

That's a huge win.


Is it? It suggests that typical Java code is for some reason full of run time type errors which it isn't. Whatever the differences between and respective advantages and disadvantages of Java and C++, runtime type errors are just not a some typical Java problem, let alone one that C++ solves.


>which it isn't

NullPointerException is a run time type error and quite common. I'm not convinced it's a problem C++ adequately solves, though.


You can take a view of types that is broader than the one Java actually uses such that an NPE is a 'runtime type error' but you can do that with lots of runtime errors. It's not what the poster is talking about.


You sure could -- Coq programmers might say that any error is a runtime type error.

I don't think NPEs go much beyond Java's own definition though. If you have a reference to a String, it's not necessarily of type String. You'd get a runtime error if you try to treat it as such, just like you would in Python.

This is not the case in C++, though obviously it's not an example of template metaprogramming.


I'm guessing this is related to the "syntactic sugar" that generics use in Java? If so isn't this largely covered by the warnings from a decent IDE?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: