I suspect this to have been proposed in some form or other before, but I couldn't find anything. It is also completely from a users perspective, as I am not an expert on implementation details. This may also already be possible in some form, in which case I apologize for my ignorance.
In short, I want to be able to classify a variable passed to a function as const, without having to define it const:
void f(int){}
int main()
{
int i = 0;
f(const i); // requires the function signature of f to be such, that i cannot be modified by it. So passed by value or const ref.
}
In this example it is pretty unnecessary, but it could be useful in certain situations.
The idea is that the interface of the called function is more explicitly specified by the caller, which could make it easier to find errors introduced by a change in the interface of a function.
some cases:
1) The most useful situation I can think of is when calling dependent functions within templates:
template<class T>
void f(T& t)
{
X x{};
auto y = t.g(x);
... do more stuff with x, y, t
}
In principle, it is unclear whether g modifies x or not. It could easily take a reference and change x. We would have to depend on the name of the function or check all possible T used with f to be certain.
So the modified line would be:
In this case it would implicitly constrain the possible types of T. It would also not make x a constant, so it could still be modified in the rest of the function.
2) It could help specify overload resolution with forwarding references (note: This may be a little artificial, I have not met the problem in code).
struct Z{};
void f(const Z& z);
template<class T> void f(T&& t){...}
int main()
{
Z z1{};
const Z z2{};
f(z1); // calls template with T = Z&
f(z2); // calls f(const Z&)
// f(const z1) // would call f(const Z&)
}
In both cases the same could be achieved with a const& alias, but that would be less easy to read and might introduce runtime overhead (possibly extra pointers?).
I think it could also be achieved with metaprogramming or concepts.
Another alternative might be to use casts to const&, although that would definitely impede readability. I'm also not completely certain about the implementation of casts, but if it works like other function-calls, this may create extra indirection through more pointers.
- It would not introduce any new keywords, and it would not break any existing code, as const can already not be used for names.
- It might decrease readability if used excessively.
- However, it might also catch some errors. In general, it would be a way to document programmer intent in code, which would be checkable by a compiler. The alternative of using an alias might actually be a way to implement this, but then there might be runtime overhead. In principle, this should be a compile-time feature only.
- I have not thought too much about pointers as I try to avoid raw ones, but I guess a const would imply "const* const" and a mutable the other extreme of "*"
Going further, a similar feature might be useful for cases where we want to specify a function to be expected to modify an object. This might again be useful in template code, where we do not know the exact interface to the called function. The keyword is a little less clear, but mutable might work, also the best would be something like out, as in c#, but that would introduce a new keyword. The implication would be that the function called must be able to modify the object, which excludes pass by value or const&.
Another direction to go is to also allow the specifier on the function call itself:
void f(T& t){
t.const g();
}
which might again decrease readability but increase compile-time checks. As const-correctness is supposed to relate to the external state of an object, this would express, that we do not expect the behaviour of the object t to be changed after this call.
Again something like mutable might be useful to specify when we expect a mutator.
All in all, I see this as mostly useful in template-code, where the exact signature of a called function might be unknown, and to increase compile time checking of expected behaviour of functions.