Never redefine an inherited default parameter value (1997)
cpptips.com
cpptips.com
default parameters are caller's hint, it's even part of declaration - not definition (and it doesn't have to be the same in different files, it's not even ub - because it's literally hint for the caller when param isn't there)
"dynamic default parameters" would pin them to the function, which isn't the design
Rectangle *r = new Rectangle();
r->draw(); // calls Rectangle::draw(GREEN)
Shape *s = *r;
s->draw(): // calls Rectangle::draw(RED)
Intuitively, you would not expect that a function changes its behavior when it's called from a pointer of a different type. I can easily see this leading to very a fun-to-reproduce-kind of bug.Though one interesting use for this, you could use this behavior to encode which class the caller uses to call your inheritable method, but it's already sounding pretty gross.
Your best bet is to use `-Weffc++ -Werror` then. I'm not certain it's checked but I'd bet it is.
1. be added as a feature that actually is defined and works
2. throw an error at compile time
What are we supposed to do, cancel our subscription to C++? Complain to the CEO of C++?
What I'm genuinely interested in, is why is this such a big deal for me, but apparently not for other people? What copes do people employ to get over it to enable themselves to write C++? Per my initial question: why (and how) do people put up with it?
Especially when modern and powerful alternatives exist which have mostly either learned from the mistakes of C++, or avoided them altogether.
1. I use a small amount of the surface area of C++, so the parts I don't use don't bother me
2. I may have silent footguns all over my code, but if I run my CMake build, and the tests pass, and the application does what I need from it, I really don't care
3. The up-front cost of writing correct/no-footgun code using Rust (as just one example) for me is much worse than the fluid "anything goes" of C++
>What copes do people employ to get over it to enable themselves to write C++?
C++ is powerful, flexible, and ubiquitous. Many third-party libaries in my daily toolbox are in C++, and I can re-use my code on Android, WebAssembly, cli applications, and any kind of project I may need to deploy. C++ skills are more widely available, such that maintaining C++ code is easier for companies (hiring employees, contractors, etc.) Delivering C++ code has me access to more clients and projects.
I can finish a C++ audio project for a client faster than I can figure out a `Ref<Arc<&'a static mut`, and they're happier for it.
That said, I don't start new projects in C++ mainly because of the header/source split. After writing a lot of code in languages that don't require a header file, I believe that C/C++ not making headers files redundant in 90s itself has been a costly mistake. Think of the time wasted because of constantly having to sync header/source files. One can argue that it does not take much time but it is hard to estimate how many bugs might be introduced because of such busywork breaking mental flow.
Oddly, real problems like these are never brought in these "C++ sucks" discussions.
The reason to use c++ is for low level performance, but it's not 2005, you can now get the same performance with modern languages that give increased safety, developer velocity, readability, maintainability, and more.
To me using default parameters to control behavior in OO languages is insanity. That’s what OO design is for?
Or make a “simple” method with a few parameters that calls an “_ex” version with the full set of parameters. (To borrow Microsoft terminology)
Sure, a little extra typing but it separates the common and special cases, perhaps inviting scrutiny on why the nondefault parameters are needed…
Soon after that, one will probably refactor all this away too.
What’s hilarious here?
But we all went away from this because pure OO is also pitfall riddled and extremely clunky and overly verbose and repetitive at a large scale.
The answer is a more functional style, partial application and immutability solves so much of what people are talking about, but nobody wants to learn to use functional languages.
People still use default arguments in functional programming when they can: see for example functions that take a map of arguments that’s merged into a default one.
That's because the current behavior is intuitive to any programmer who understands the core design decisions of C++. Default parameters don't change the signature or name mangling of a function, and they have to be specified in the declaration rather than the implementation. So without being told, it's clear that compliers must handle them at the call site.
At the call site if you only know the object is of type Shape, the compiler can't possibly insert the default value for type Rectangle. Heck, in the general case Rectangle might be defined in a separate DLL or .so file that doesn't get loaded until run-time. So there's no possible way for the compiler to know its default value here. So without being taught, C++ programmers know what behavior to expect, and they're right.
That said, over time the use of default parameters has gone from encouraged to discouraged, largely to promote code clarity and explicitness. So it's a less important feature now than it was in 1997.
Eh, that's not exactly true. The ABI for C++ could have included a way to look up the default values of defaulted parameters from the object's run-time class.
But the C++ ABI didn't need that. Oh, and there's no C++ ABI, but this behavior would have been part of the de facto sort-of ABIs if it had been required by the spec (but it wasn't).
That said, I agree with you that this is intuitive.
It won't solve the problem, but it is a much saner behavior.
If you want the expected behavior, that is that a call through a base class pointer uses the default parameters of the derived class, then you shouldn't use default parameters at all. Use overloading instead.
Couldn't you store the default value in the same place you store the function pointer? I don't see why it would cost you anything at runtime.
If you like, consider it to be 2 different functions. One takes an argument, one has a fixed value.
Anyway, it's more than one way antithetical how default arguments work in C++. It's not Python, C++ doesn't have default argument objects in any shape and form. Default argument expressions are expected to be evaluated at call time, with all of its observable implications. The feature itself doesn't mesh well with virtual functions, but there is no reasonable way to resolve it. You either break the intuitions for virtual functions or to default arguments.
virtual void foo(std::vector<int> x = {10, 20, 30});
You can't just store a single std::vector{10, 20, 30}. You would need to store a (virtual) factory function instead which, while doable, adds more overhead.