Thanks for all the comments - I'd like to expand on a few of them.
When I wrote the last post, I was thinking of cases where you have a choice between languages, so I didn't think of cross-platformk, since if you need to run on a platform where a language isn't present, that pretty much eliminates the language from consideration.
It's true that C++ is available on far more platforms, and if that's important in your case, C# probably isn't an option for you.
Templates, template metaprogramming, STL, Boost
I missed templates as an advantage that C++ currently has, as I forgot that Whidbey isn't there yet for C# programmers. When Whidbey is widespread, C# will have the majority of the features that I'd want related to generic types, though it won't be able to do as much as C++ does.
In my mind, that's (mostly) a good thing. While there are things that aren't in C# generics that I'd like, I think that, because of the indirection involved, generic types are something that are best enjoyed in moderation, as they're near the limit of what most programmers can easily understand. Which brings us to template metaprogramming. The discussions I've read on this topic list "power" and "optimization" as the big advantages of this technique, and I'd have to agree with that evaluation. But the code that I've looked at makes normal template code look simple and straightforward. So, I'm not sorry that you can't do this with C# generics.
Something I do miss is the ability to do Mixins, which would be a nice complement for a language without multiple inheritance. They would be helpful to add in system functionality without burning the base class.
STL isn't the kind of library that I like to use, as I think it's too baroque. Sure, you can do a *ton* of things with it and easily switch things around, but I've never found that I need to switch things around that often, so it's complexity that I don't use, but still have to deal with. So, for me, no thanks - I'd rather have foreach, which covers about 90% of my loops. Oh, and before I leave this topic, I should mention that the richness of data structures in STL is a lot greater than that in C#, though you should keep your eye out for C5 and PowerCollections when Whidbey shows up.
In the current C#, foreach only supports one way of iterating. About 3 years ago I wrote an article on some collection wrappers you could use to support other ways of iterating, though at some cost to performance. Unfortunately, I chose to call the "iterators", which, of course, is also the name of a C# 2.0 feature that allows you to make objects iterable more easily, and support multiple ways of iterating a collection.
Boost seems like an obvious C++ advantage, if you're working in an environment where you can use outside libraries.
There were a lot of comments around deterministic destruction, and there is certainly a big difference between the "programmer owns the allocations" and the "the GC owns the allocations" approaches.
I will admit that when I first started using C#, I missed that feeling that I had full control over what was going on in the system. But over time I found that while I did need to be concerned with scarce resources (db connections, file handles, and other system resources), I didn't really need to be spend a lot of attention on memory resources. For scarce resources, "using" works well for me, and I prefer the scoping that "using" gives me over the scope-based lifetime that you get with smart pointer approaches in C++, and I also like that it's more explicit.
Oh, one other point on object lifetime. Having an environment where there is no automatic scope-based lifetime makes supporting exceptions much cheaper in C#, as there isn't the overhead of tracking what objects are live at any point that is required by C++ exceptions.
Which brings us to const. My experience with const is as follows:
When I used const in my projects, I always ran into situations where I needed a routine that was const to become non-const. That meant either changing that routine - and then updating all of the callers so that they were non-const - or creating non-const versions of existing routines where applicable. Neither of those is a particularly nice and/or fun thing to do, and after trying it for a while, my conclusion was that having const didn't give me enough benefits to make it worth the disadvantages.
I do agree that const can give some protection against the programmer doing the wrong thing (which, interestingly, is not really in keeping with the general C++ philosophy that programmers should be able to do whatever they want, even if it's wrong (yes, I'm being a bit extreme there)), but since it's merely a convention and not a guarantee (as I can cast const away whenever I want, or just use "mutable"), I don't see a lot of value.
I've talked to enough people to know that my opinion is not shared by all.
C# things I missed
There were a couple of notable things I missed from my C# list.
Events are much much more useful than I had originally thought. While you can do a lot of similar things with interfaces, events are great for the sort of loosely-coupled components that I like to create. For me, events are a feature that work exactly the way I want them to.
This is a big one that I missed.
In C#, there is one string type.
In C++, I'm currently dealing with code that uses:
- CString (the ATL/WTL type)
and needs to transform strings from one type to another fairly regularly. I also spend time making sure I have the right distinction between byte count and character count when dealing with such types.
If I've missed any that you'd like me to comment on or you have others to talk about, feel free to pile on...