CppCoreGuidelines icon indicating copy to clipboard operation
CppCoreGuidelines copied to clipboard

Explain freedoms available in F.16-F.18

Open russellmcc opened this issue 5 years ago • 2 comments

As I discussed in #1407, I found the existing wording confusing as I expected that the guidelines would provide me with one recommended way to author function arguments in all situations. However, the discussion on the issue clarified that the intent was to allow many ways to author functions, and only to disallow specific clearly non-optimal cases.

This is my attempt to reduce that confusion, by explicitly calling out when such freedoms exist.

As I mentioned also in #1407, I'm somewhat doubtful that providing this much developer freedom is a good idea. A different way to solve this confusion would have been to provide an explicit recommendation for these ambiguous cases. However, I don't feel especially qualified to provide such a recommendation, hence this PR.

russellmcc avatar Apr 28 '19 21:04 russellmcc

Editors call: Thanks, we've reviewed this and we think we see where you're coming from.

We believe that the guidance does actually provide a deterministic decision tree for how to write parameters. Note that each Item is about "what I want to do with the argument" -- e.g., "in" means "I want an X I can read from." So for example the last edit for F.18, which correctly notes that const& and && overloads can be used, doesn't actually belong in that item because F.18 is intended to be about "my functions wants an argument I will move from," and in that case there will not be a const& overload. Rather, the const&/&& overloading comes from the "in" parameter (F.16), where we use by-value or by-const& by default, then only if we want to optimize we can add a && overload. So we think that the guidance as written is correct, based on each Item being about wanting to do a specific thing with the argument.

With that in mind, are there other changes that would make that clearer?

hsutter avatar May 02 '19 18:05 hsutter

Thanks for your time. I really appreciate the consideration and thought, as well as the rest of the work the editors do on this document.

I completely see your point regarding my note in F.18 being inappropriate. I'll drop that part without further comment.

However, unfortunately, I just can't agree with you that the guidelines as written provide a deterministic method for choosing a function's signature. As I mentioned in the discussion on the linked issue (#1407), many rules can apply to a given scenario, and the exact set of signature(s) you end up with depends deeply on which rule you choose to apply.

struct A { vector<int> data; };
struct B { vector<int> data; };


// Follows F.16 - no optimization
B convert(const A& a) {
 return B{a.data};
}

// Follows F.16 - with optimization
B convert(const A& a) {
 return B{a.data};
}

B convert(A&& a) {
  return B{std::move(a).data};
}

// Follows F.18
B convert(A&& a) {
  return B{std::move(a).data};
}

// Follows F.19
template <typename A>
B convert(A&& a) {
 return B{std::forward<A>(a).data};
}

So, from the same starting point ("I want to convert A to B"), the guidelines can point me in four different directions regarding the type signatures in the overload set. To me, seeing the chart after F.15 made me think there would be a single recommended overload set for each scenario. This is what my edit was trying to clarify.

I'm totally open to the idea that I'm missing something about how I'm supposed to think about this set of guidelines - if I am, please let me know!

russellmcc avatar May 02 '19 23:05 russellmcc