4 ms·
Oh, wow. I know most people dislike C++ language extensions, given how complex the language already is. But as a library author, I truly believe this is the mos
by byuu 12y ago
Oh, wow. I know most people dislike C++ language extensions, given how complex the language already is. But as a library author, I truly believe this is the most powerful and useful addition we can add to the language now. I've been wanting this exact thing for years, but unfortunately you can't even do it as a preprocessor due to the complexity of ADL and SFINAE on lookups.
First, I like Herb Sutter's propsal more ( http://isocpp.org/files/papers/N4165.pdf http://isocpp.org/files/papers/N4165.pdf ), as Bjarne's proposal to favor x.f() when encountering f(x) would be a breaking change. However, Herb's suggestion to accept x in any position in f (eg x.f(a, b) could match f(a, x, b)), in order to wrap libc interfaces such as FILE* more easily, I feel is a mistake. I'd rather we keep the complexity of parsing down and write wrapper headers around libc to support the new syntax instead (which is also a wonderful opportunity to get the functions and #defines out of the global namespace.)
Onto utility ... as stated, nearly all of my time writing C++ is spent creating libraries. This is always a huge struggle: you don't want users to have to remember that some functions are class::func() and others are func(class&), and indeed it makes for some really ugly code, eg c(b(a()) instead of a().b().c(). Further, since C++ classes cannot be reopened, it's a real problem to just rely on users to extend your classes to add desired features. Person A makes class fooObject : object, person B makes class barObject : object. But now fooObject and barObject are incompatible, unless you slice back to object, and lose the whole point of your extensions.
Unified function call syntax (UFCS) solves this nicely: member functions are now only those that need access to private state. Your classes are much smaller, which aids in encapsulation. Further, you don't have to code everything but the kitchen sink into your classes anymore, the user can simply add functionality that they need. Or you can offer the extensions piecemeal in separate headers. So not only do we get UFCS, we also get a nicer form of C# extension methods for free.
Further, it's a real boon to IDE auto-completion features to be able to know available functions after object.[function] than it is after function([object]; the former is much easier. Imagine an IDE trying to auto-complete "begin(", for instance. That list is going to be hopelessly long.
Lastly, it will be very interesting to see what rules they put in place around this and primitive types. If you can have square(int&), then you could call int x = 5; int y = x.square(); But even more interesting will be if this is allowed for constants: int x = 5.square(); string y = "hello".toUppercase(); ... there is the potential to allow C++ to be a truly object oriented language where everything is an object, if this is done right.
On that note, I haven't seen much talk around the implementation of f(x), but they definitely need to allow for both x::f() -> f(x&) and x::f() const -> f(const x&). I'm also a little concerned about x->f() -> f(x*), and how that might play in with overloaded operator-> that you find in smart pointer classes.
For those in favor of f(x, y) over x.f(y), please consider the history of ambiguity around operator arguments. strcat is (target, source) whereas rename is (source, target). There are thousands of examples like this. Putting the target before the function call is an absolutely wonderful way to remove the burden of having to remember each and every function's exact ordering. For cases where there is no clear "target", regular function call syntax can still be used, eg intersect(x, y)
But, complexities aside ... I really hope this feature makes it in, and that the naysayers to all language extensions do not ruin this. It will completely change the way I program in C++.
- angersock 12y agoWhat is a use case in your library code where you have the problem of class::func() vs func(class&)? I'm willing to wager your API is just plain written badly in that case. It will completely change the way I program in C++. This is a bug, not a feature. I kind of think, at this point, Bjarne is trying to get the language to implode once and for all so he can finally get some rest--and only has to take such drastic measures because the community is too enamored with shiny to just let the old dog die.
- byuu 12y agoI must not have explained it well enough. Take std::string. It is missing majorly useful functions that exist in other languages, such as trim, transform, split/explode/tokenize, replace-by-string (std::string is replace-by-length, which is more like memcpy), etc. You can add those functions. But you can't add them into std::string. So you get: void trim(string& s, const string& trim); And now your code is a mix of two styles: s.append(" Hello, World "); trim(s, " "); s.compare(...); As I've explained, subclassing is dangerous. Two people each add their own functions to their own derived types, and now those types aren't directly compatible without slicing back to std::string. So the problem, as a class author, is that you want to add all of the functions a user might want, so that they can consistently use the same syntax. So when you write your string class, users can then use it like so: s.append(" Hello, World "); s.trim(" "); s.compare(...); //or even possibly ... s.append(" Hello, World ").trim(" ").compare(...); (You may say the syntax difference doesn't matter, but I disagree strongly: it's burdensome to remember which functions are string::foo(), and which functions are foo(string&); and if I wanted to use foo(string&) exclusively, then I'd go back to writing in C.) This causes substantial bloat by packing string with a ridiculous number of functions. Some of those functions are going to be very niche. For instance, I have find/replace/split functions that ignore values inside of quotes, which I use extensively in a cross assembler I wrote. But they're not very useful for my other programs. It would be nice if I could #include <string/extension/quoted-strings.hpp> for the cross assembler, which would contain UFCS functions, and leave this out of my other projects. Smaller compilation times, and easier to create experimental/fluid extensions before committing them to the official class API. And if a user of my string library decided that they needed additional functions, they too could add them. I would go so far as to say that UFCS will result in more developers using the C++ standard library containers and types, rather than rolling their own. Which would be a huge win for the language.