CppCoreGuidelines
CppCoreGuidelines copied to clipboard
Relax enforcement of rule F.16
We have implemented an automatic check for the current enforcement of rule F.16:
- (Simple) ((Foundation)) Warn when a parameter being passed by value has a size greater than 2 * sizeof(void*). Suggest using a reference to const instead.
- (Simple) ((Foundation)) Warn when a parameter passed by reference to const has a size less than 2 * sizeof(void*). Suggest passing by value instead.
We have received a lot of comments on this by users. Basically it comes down to 2 objections:
- The current enforcement is black or white, either something something must be pass by value or must be pass by reference unless the size is exactly 2 * sizeof(void*). The border between when to pass by value or by reference is not that strict in practice. This should also be reflected in the enforcement rules. E.g. greater than 4 * sizeof(void*) -> pass by reference and less than 1 * sizeof(void*) -> pass by value. If your type is somewhere between these two borders, you are allowed decide for yourself whether to use pass by reference or pass by value.
- Passing by reference is always OK. The overhead of passing by reference is negilible. So why not only demanding the first enforcement? This is because passing a huge object by value should certainly be forbidden, whereas passing a small object by reference is not wrong.
What are your thoughts on this?
Regards,
Paul
I think, much more important than the size is whether the type is trivially copyable or not. For trivially copyable types, I find the rule of thumb in the rules totally fine. F.ex. you definitely want to pass a pointer or a size_t by value, which wouldn't be covered by "less than size(void*)". [EDIT: for non-trivially copyable types, sizeof has little meaning for determining the cost of copy and the default should imho always be pass by const&]
As always, the important thing is that there is a way to suppress it when I have hard data that breaking this rule is important for semantic or perf reasons.
Passing a huge object by value might make sense if it permits guaranteed elision, rather than forcing early temporary materialization in order to bind a reference to the temporary.
Unfortunately, the right rule is probably "it depends" and any inflexible enforcement will be wrong in some cases.
I don't think passing by reference is always fine. It's not just a question of cost but complexity. When I pass something by reference I don't know how long I need to keep the referenced object alive for. Writing a function that takes an object by const reference and requires the object to outlive the method call is probably a bad idea but it can be done and when I'm calling someone else's (including my past self) code I don't know how many bad ideas they acted on. That doesn't mean you should never pass by const reference but it is a reason to prefer passing by value.
Editors call: We agree that every absolute size is unlikely to be always correct. But we don't think that passing by reference is always acceptable (e.g., sometimes it creates a double indirection, sometimes potential aliasing inhibits optimizations). We will review this issue again when we review the general issues of pass-by-value.
Do I understand correctly that all fundamental types are considered "cheap to copy"? If so, could this be mentioned explicitly, directly after the line, "What is "cheap to copy" depends..."?
https://github.com/isocpp/CppCoreGuidelines/blob/ddef6cdbaeba9ec70e58e52eeb54635c0f3f0804/CppCoreGuidelines.md?plain=1#L2901
Proposed addition:
An instance of a fundamental type is usually "cheap to copy".
PS Sorry if I should have posted this question at issue #1421 ( F.16 what exactly is cheap to copy for the purposes of passing "by value"? ) instead