1. The problem statement is well-defined and C++ is the language that cares about these details (compared to, e.g., Java or even Rust).
2. The author correctly identifies a feature in the language that solves the same (or a very similar) problem in a different context with a very hard-to read mechanism invoking special rules.
3. The author proposes additional syntax (essentially necessary for backwards compatibility) that allows to apply the existing mechanism in a new context.
4. The author proposes a new (set of) rule(s) that gives meaning to the new syntax using the existing mechanism.
In sum we have:
* 1 piece of new syntax
* 1 new semantic rule
* offloaded a limited problem to a the ever more complex template instantiation machinery
* less code in implementations, more in headers
Note that the original use case was about how the this argument is passed to methods. But it became a very powerful language extension. (See section 4.2.2) This reduces usability. Imagine a developer calls a method with the wrong const qualifier, yielding an (unexpectedly) const-qualified return type. Currently, there will be a short and helpful error message. With this proposal, this error message will be buried in the template instantiation details.
The problem is that template instantiations have become so generic that you essentially program the compiler with them. But how to generate meaningful error messages remains a mystery. Presumably this will be left to the library authors then.
Agreed that modern c++ has bloat. With that said, I believe that the use case that you're describing can be solved by using concepts to constrain the "Self" template parameter, instead of allowing it to be an unconstrained generic type.
I don't think so. Before: "You got a const value /here/ because you called /this/ function which is declared to yield a constant /there/.". Now: "You got a const value /here/ because you called /this/ function with /that/ (huge) template argument we have deduced for you and by a process that is essentially a complex algorithm we think that the return value is const." The function is not going to be constrained because the author wants it to be very flexible.
Hmmm I'm trying to imagine the scenario. It sounds like this:
// Structure with a function `str`, as in the paper
struct StrHolder {
// Assume `str` returns a `const std::string&` when `this` is const
// and `std::string&` otherwise.
template <class Self>
decltype(auto) str(this Self&& self) { ... }
};
// Let's use it here
void main() {
const StrHolder s;
s.str().size(); // OK; calling a const function on `const std::string&`
s.str() = "abc"; // Ill-formed; compilation error here
}
Did I get the scenario right?
If so, from what I could tell from this example, I think the error would essentially amount to "can't call operator= on const-qualified `const std::string&`".
After that, it would depend on the compiler in terms of how it prints out template "frames". On Visual Studio, it would probably look pretty ugly. On Clang, might be a bit better. I don't recall how G++ prints it out. After the first line, I think it would look mostly like how any template function is expanded, which is... not that pretty.
All in all, I agree it would be a bit uglier than a plain const decorated function, but I don't think that it's bad enough to dismiss the feature, though I admit that this is subjective.
This feature will greatly simplify CRTP while also making it safer and more powerful.
It will also remove the need to of multiple pointless copy pasted overloads with different reference qualification on this.
On the balance I think the change will be an improvement.
Concepts could be used to make the error messages better, but TBH I don't have high expectations on concept error messages (they are still useful for other reasons though).
edit:
> Currently, there will be a short and helpful error message. With this proposal, this error message will be buried in the template instantiation details.
if there is a single overload, there is no point in using this feature. If there are multiple overloads, the error message is already not going to be very nice.
edit2:
In retrospect I think that the language should have had explicit 'this' from the beginning a-la python, with no difference between member functions and free functions (except for virtuals possibly) and '.' should have been just syntactic sugar. IIRC Bjarne thinks the same.
You are right in the observation that it will save some lines of code to be /written/. Whether or not that's an essential improvement for the writers of code, I don't know (one must weigh the cost of knowing the feature/pattern in the first place).
But this perspective is essentially my grievance. C++ turns more and more into an exercise of programming the compiler. usability for people that want to use code is ever more deteriorating. There are more aspects like this in the language, for instance the fact that not even the std library can write out the return types or argument types of many functions. Or the hoops one has to jump through to deconstruct a std::variant (no one dared to add a proper sum type, but it is ok to implement one using the template interpreter).
Well, I'm not completely disagreeing with you, but by experience, less line of code often translates in simpler to understand code in C++ if the removed code is just boilerplate (i.e. you are not just golfing).
Concepts do help with the return type issue, at least now you can declare that your inferred return type conforms to some interface; for example:
auto iterator take_n(auto range&& r, size_t n) {...}
take_n is very generic and can return any type. But at least with concepts you can declare that it will look like an iterator. (in practice the result type depends on the range type and take_n declaration will show that).
One problem with C++ is that the barrier for adding stuff to the standard library is much lower than adding it to the language. So for example we got std::bind well before we got lambdas; same thing will happen with variants (already in the library), proper union types and patter matching (there are proposals to add both the language).
In my experience, new C++ programmers struggle with build and packaging systems a lot more than they do the arcane error messages. Compile times for established codebases also cause frustration for newcomers.
Bad error messages are also a frustration, but it's not the worst part. In those cases, they can look for examples, read guideline documents (books, really), and play around on compiler explorer to get familiar with idioms.
Rust doesn't care about details? Have you used it? :)
I've found C++'s templates to be both less strict / less well-defined and in many ways more powerful than Rust's template system. Rust's template system is fully typed and has some important restrictions, the most significant being that it's hard to template on multiple different types and it's hard/impossible to template on integers or other concrete types. C++'s template system is a glorified preprocessor, giving you much power but also an enormous Howitzer foot cannon.
I did use rust and I never came across a template system ;).
However, rust makes certain design choices that C++ leaves open. They aren't too dissimilar in many aspects, and rust definitely is the sound of the two languages. But what I meant was that in C++ there will always be a very precise distinction between the tiniest nuances of how code behaves because the language wants to allow that level of control.
Take for instance the difference between assignment and construction. Another language might decide that one is explained by the other and be done with it. C++ makes a distinction.
As someone who jumped on the C++ train with C++98 and did my best to keep up since then, at some point around C++17 I gave up out of the sheer headache of having to reread entire manuals about how to avoid pitfalls any time I have to do more C++. None of the other languages I use exact such a heavy toll when you're rusty and need to reacquaint yourself.
I've written more C++ since C++17 because that in those cases it was the best tool for the job, but, man, every time I do it I feel a strong urge to just burn it all down.
> The problem statement is well-defined and C++ is the language that cares about these details
It's an actual programming langauge (rather than a research project) so includes a number of convenience functions and guard rails. Thus 'final' lets you tell the compiler your intention (this is "caring about the details", a kind of guard rail) yet the compiler will already synthesize a bunch of functions for you, either completely (e.g. default constructors) or partially (selecting which template to expand). You can dispute the branch cuts but can't claim that a choice is inherently wrong.
C++ "suffers" because of a very strong (but not absolute) commitment to backward compatibility.
One reason the standard seems bloated is it includes a lot of features that are mostly or even entirely for library writers, as well as a lot of features for system programming not used by most developers. You can write your Java runtime in C++ but you can't write the C++ runtime in Java. They languages are intended for different purposes and in that regard you can't really say one is "better" than the other.
I consider features like this to be in the "reduce programmer opportunity for error" class of improvement, which I personally favor.
To submitter (ingve): In the title, "this" is a C++ keyword that shouldn't have been capitalized to "This".
I also agree with sibling comment that an alternative title such "Deducing this (A C++ syntax proposal)" would save a click for many readers who aren't interested in C++.
it hosts the homepages of a bunch of ISO working groups producing standards for software (ISO C, C++, ADA, POSIX for example). Open because the try to keep the process in the open (as much as ISO allows), sharing publicly at least the working papers and proposals.
Interesting. But I cannot judge if further syntax is worth the suggested improvements. Is it okay to say that it is better, that the committee decides that for me? I would appreciate actual UTF Support within language more ;)
20 comments
[ 3.3 ms ] story [ 35.6 ms ] thread1. The problem statement is well-defined and C++ is the language that cares about these details (compared to, e.g., Java or even Rust). 2. The author correctly identifies a feature in the language that solves the same (or a very similar) problem in a different context with a very hard-to read mechanism invoking special rules. 3. The author proposes additional syntax (essentially necessary for backwards compatibility) that allows to apply the existing mechanism in a new context. 4. The author proposes a new (set of) rule(s) that gives meaning to the new syntax using the existing mechanism.
In sum we have:
* 1 piece of new syntax
* 1 new semantic rule
* offloaded a limited problem to a the ever more complex template instantiation machinery
* less code in implementations, more in headers
Note that the original use case was about how the this argument is passed to methods. But it became a very powerful language extension. (See section 4.2.2) This reduces usability. Imagine a developer calls a method with the wrong const qualifier, yielding an (unexpectedly) const-qualified return type. Currently, there will be a short and helpful error message. With this proposal, this error message will be buried in the template instantiation details.
The problem is that template instantiations have become so generic that you essentially program the compiler with them. But how to generate meaningful error messages remains a mystery. Presumably this will be left to the library authors then.
If so, from what I could tell from this example, I think the error would essentially amount to "can't call operator= on const-qualified `const std::string&`". After that, it would depend on the compiler in terms of how it prints out template "frames". On Visual Studio, it would probably look pretty ugly. On Clang, might be a bit better. I don't recall how G++ prints it out. After the first line, I think it would look mostly like how any template function is expanded, which is... not that pretty.
All in all, I agree it would be a bit uglier than a plain const decorated function, but I don't think that it's bad enough to dismiss the feature, though I admit that this is subjective.
It will also remove the need to of multiple pointless copy pasted overloads with different reference qualification on this.
On the balance I think the change will be an improvement.
Concepts could be used to make the error messages better, but TBH I don't have high expectations on concept error messages (they are still useful for other reasons though).
edit:
> Currently, there will be a short and helpful error message. With this proposal, this error message will be buried in the template instantiation details.
if there is a single overload, there is no point in using this feature. If there are multiple overloads, the error message is already not going to be very nice.
edit2:
In retrospect I think that the language should have had explicit 'this' from the beginning a-la python, with no difference between member functions and free functions (except for virtuals possibly) and '.' should have been just syntactic sugar. IIRC Bjarne thinks the same.
But this perspective is essentially my grievance. C++ turns more and more into an exercise of programming the compiler. usability for people that want to use code is ever more deteriorating. There are more aspects like this in the language, for instance the fact that not even the std library can write out the return types or argument types of many functions. Or the hoops one has to jump through to deconstruct a std::variant (no one dared to add a proper sum type, but it is ok to implement one using the template interpreter).
Concepts do help with the return type issue, at least now you can declare that your inferred return type conforms to some interface; for example:
take_n is very generic and can return any type. But at least with concepts you can declare that it will look like an iterator. (in practice the result type depends on the range type and take_n declaration will show that).One problem with C++ is that the barrier for adding stuff to the standard library is much lower than adding it to the language. So for example we got std::bind well before we got lambdas; same thing will happen with variants (already in the library), proper union types and patter matching (there are proposals to add both the language).
Bad error messages are also a frustration, but it's not the worst part. In those cases, they can look for examples, read guideline documents (books, really), and play around on compiler explorer to get familiar with idioms.
I've found C++'s templates to be both less strict / less well-defined and in many ways more powerful than Rust's template system. Rust's template system is fully typed and has some important restrictions, the most significant being that it's hard to template on multiple different types and it's hard/impossible to template on integers or other concrete types. C++'s template system is a glorified preprocessor, giving you much power but also an enormous Howitzer foot cannon.
However, rust makes certain design choices that C++ leaves open. They aren't too dissimilar in many aspects, and rust definitely is the sound of the two languages. But what I meant was that in C++ there will always be a very precise distinction between the tiniest nuances of how code behaves because the language wants to allow that level of control.
Take for instance the difference between assignment and construction. Another language might decide that one is explained by the other and be done with it. C++ makes a distinction.
I've written more C++ since C++17 because that in those cases it was the best tool for the job, but, man, every time I do it I feel a strong urge to just burn it all down.
It's an actual programming langauge (rather than a research project) so includes a number of convenience functions and guard rails. Thus 'final' lets you tell the compiler your intention (this is "caring about the details", a kind of guard rail) yet the compiler will already synthesize a bunch of functions for you, either completely (e.g. default constructors) or partially (selecting which template to expand). You can dispute the branch cuts but can't claim that a choice is inherently wrong.
C++ "suffers" because of a very strong (but not absolute) commitment to backward compatibility.
One reason the standard seems bloated is it includes a lot of features that are mostly or even entirely for library writers, as well as a lot of features for system programming not used by most developers. You can write your Java runtime in C++ but you can't write the C++ runtime in Java. They languages are intended for different purposes and in that regard you can't really say one is "better" than the other.
I consider features like this to be in the "reduce programmer opportunity for error" class of improvement, which I personally favor.
I also agree with sibling comment that an alternative title such "Deducing this (A C++ syntax proposal)" would save a click for many readers who aren't interested in C++.
(jk in a sad way though)