Wajideus wrote:
It wouldn't hurt if the compiler made function arguments const by default with the exception of dereferenced pointers. I can't think of a situation where I've ever needed to reassign one of the arguments to a different value.
Also, I think a lot of the const-madness could be avoided if the language allowed captures to be used to purify the scope of any block and if there was better support for memory ownership semantics and compile-time evaluation.
The scenario you unfolded makes it pretty apparent just how much of an anti-pattern that const-correctness really is.
Sorry for getting back on this a bit late, I got distracted by Zaval's post.
I am not sure if I understand what you mean here, and I suspect you may not quite get what Solar was saying.
To clarify: 'const-correctness' is an
idiom and coding pattern used primarily in C++, regarding the passing of arguments that are not and/or should not be modified by the called function, or any functions it in turn calls.
It is the principle that if the value of a formal parameter to a function (the variable declared in the function's signature)
can be a constant, then it should be declared so explicitly in the function's signature and prototype, so that the compiler can enforce constancy, and check that the arguments (the actual values being passed) are in fact constants.
The second part is the key factor regarding Solar's rant, because it relates to have C++ handles the types of parameters and their corresponding arguments. The general principle of const-correctness (that constant parameters should be declared as such) interacts with the way C++ handles type enforcement, and the different
kinds of constantness it allows for.
Now, just for some background, in C (at the time C++ was designed) all parameters are passed by value - a copy of the argument is passed to the function to fill in the parameters, and initially, only scalar arguments could be passed. Furthermore, in very early versions of C, the parameter types were optional - the function signature consisted of just the names of the parameters, and the types came after the signature like so:
Code:
foo(bar, baz)
int bar, char baz;
{
/* code goes here */
}
Worse still, most C compilers - including the Bell Labs/Unix one - didn't actually enforce argument types or even parameter
arity; since the arguments themselves were just a bag-o'-bytes, an argument of a different type would (in effect) be silently coerced to the type of the parameter. Function prototypes[1], when they existed (they weren't required, being there mainly for the sake of the linker), were often just
Code:
foo();
Furthermore, only scalar values could be passed on the stack (which was the assumed method of argument passing). It became an idiom in C for structures, or out parameters (ones which were used to for parameters that were to be altered by the function) to be passed as pointers to the argument, instead.
In 1979, when
Bjarne Stroustrup started designing what would become C++ - which up until 1983 or so was just a
preprocessor for C called 'CFront' - he sensibly decided that this was a terrible idea[2] and added a lot more type checking in CFront. He also added the 'reference' type, to allow for out parameters to be passed without them being explicitly pointers, and added support for passing structures and classes directly.
However, this was still evolving well into the late 1980s, and when ISO decided that it too needed to be standardized, some time around 1993, the it was already a bit of a mess. It would only get more complicated as the standardization proceeded, as the need to clarify the interactions trumped the need for simplicity in the language.
The result of this is, as Solar mentioned, that "const int *foo;"
and "int const *foo;" are not the same declaration, the latter saying that "foo" is constant, the other saying that "foo" is a variable, but can only be assigned constant addresses.
This is where const-correctness in C++ comes in, as it is a rule of thumb about how to be consistent about what the parameters themselves should be declared as, and how to use the type-checking to makes sure it does what you want.
So, I am not sure if you are saying the this idiom is an anti-pattern in C++ programming, or if the language design 'features' that led to it are an anti-pattern for language design.
- Note also that the default type for returns was int, and the practice was that for functions that has no return, or returned an int, no return type was specified. The confusion this caused led to the first standards committee deciding that they needed a void type, something which I am pretty sure was done in C++ from the start but hadn't existed in C before - work on the first version of the C standard was started in 1985, around the same time that "C with Classes" was renamed "C++" and started drawing interest outside of Bell Labs, though the standard wasn't finalized until 1988.
- It seems obvious in hindsight, but you have to remember that in 1968 - when strong typing was a new and somewhat contentious idea - Kernighan, Thompson, and Ritchie were writing a language for their own personal use in system development, one they never expected anyone who wasn't a Bell Labs engineer to ever see it; by the time they decided to publicize it, things were already set. They resisted even simple fixes like changing "=+" to "+=" for years, and only made that particular change because it made parsing less of a mess.