isocpp / CppCoreGuidelines

The C++ Core Guidelines are a set of tried-and-true guidelines, rules, and best practices about coding in C++
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
Other
42.46k stars 5.43k forks source link

Relax enforcement of rule F.16 #1784

Open pauljansen42 opened 3 years ago

pauljansen42 commented 3 years ago

We have implemented an automatic check for the current enforcement of rule F.16:

We have received a lot of comments on this by users. Basically it comes down to 2 objections:

  1. 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.
  2. 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

MikeGitb commented 3 years ago

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.

jwakely commented 3 years ago

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.

shaneasd commented 3 years ago

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.

hsutter commented 3 years ago

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.

N-Dekker commented 2 years ago

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