Improve and reduce usage of universal reference and forward (WIP)#1401
Improve and reduce usage of universal reference and forward (WIP)#1401TonyXiang8787 wants to merge 42 commits into
Conversation
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
mgovers
left a comment
There was a problem hiding this comment.
i would argue against globally disabling the check of both clang-tidy and sonar cloud. i get that there are multiple separate reasons why one would use it, and i get that it clutters the codebase, but i generally am of the opinion that the people working for sonar and clang are probably more aware of the details etc. than we are. also, even within the maintainer team, not everyone is as aware as you are as to when which use case is used. IMO, we should better be safe than sorry.
Replying to your general comments @mgovers. The actual legitimate use of template <class... Args>
decltype(auto) some_func(Args&&... args) {
return some_other_func(std::forward<Args>(args)...);
}Even for this case, missing So the proposal to disable rules globally is based on three major facts:
And no, for this particular case, sonar and clang-tidy is wrong in big way. They simply cannot distinguish difference use-cases of universal reference in a deterministic way. |
|
@mgovers @TonyXiang8787 Can we do a knowledge sharing for everyone before we merge this? I can prepare it if so. It's good to be aligned first and make sure we all understand the subtleties well. |
The intention is indeed to discuss and agree with the maintainers, then merge (possibly with adjustments). |
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
|
A few additional points to add to this discussion:
but in practice that's not really true, as I believe with capture
struct CustomFunctor {
void operator()() & {
std::cout << "called on lvalue\n";
}
void operator()() && {
std::cout << "called on rvalue\n";
}
};
template <typename F>
void plain_invoke(F&& f) {
f(); // f is a named variable => always an lvalue expression
}
int main(){
CustomFunctor f{};
plain_invoke(f); // correct behavior, output is "called on lvalue\n"
plain_invoke(CustomFunctor{}); // incorrect behavior, output "called on lvalue\n" and expected "called on rvalue\n"
return 0;
}I just wanted to highlight a few points I came across lately for this discussion. |
It does not requires perfect forwarding to work correctly. It needs to be a universal reference, if the caller wants to pass an owning-mutable container, still no forward is needed. I will come back to this point later in the PR description. NOTE: this is under the assumption that your function (and follow-up deeper calls) does not intend to take over the ownership of the resource. We should not generalize to assume all function calls on ranges will potentially take over some ownership.
You are correct here. Generically, there can be a case where The same goes with the cheap to copy functor. It's not that functors are always cheap to copy. But I think we can define cheap to copy, pass by value, as the coding rules of PGM core. |
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com>
|



Background
This PR fixes a code quality problem. In the code base,
std::forwardis excessively used. Most of the usage is not justified, but merely to satisfy theclang-tidyandsonarrules. This PR removes those usage and adjusts the rules.Functor object as function argument should be passed by value
One misuse of
std::forwardis wrongly declaring functor object in function argument as universal reference. See below:The CPP guideline is clear: Function objects should be cheap to copy (and therefore passed by value).
Defining functor argument as reference is not correct. The PR changes this back to pass by value. See below. Note the declaring universal reference for
argsand usestd::forwardonargsis correct, which is actually the only correct usage ofstd::forwardin PGM code base.Universal reference is used as capture temporary proxy object, it should not be forwarded
The universal reference (or the confusing name forwading reference) is not only meant to be used when you want to do perfect forwarding. It has another major use-case: capturing temporary proxy object. The proxy object can be:
eigenexpression templatestd::ranges::viewswhich is constructed on the flySelf&&is declared as universal reference to be able to capture a temporary created object.In all this cases, nothing will need to be forwarded. Thus usage of
std::forwarddoes not make sense. The clang-tidy and sonar unfortunatley cannot detect this use-case, and raise false warning.We should just remove the
std::forwardhere.When to use std::forward? Why clang-tidy complains so much?
std::forwardis only justified if we expect that the follow-up function has potentially a rvalue reference overload which could consume the object. This is relatively rare in PGM. On the other hand, the use-case of universal reference to capture temporary proxy object is huge in PGM.Unfortunately, both of them do not have good mechanism to detect the other legitimate use-case of universal reference. This results in many false warning from clang-tidy and sonar. This PR proposes to silence this rule for both clang-tidy and sonar globally. Silencing it locally would create too much
//NOLINTcomment which hams readability and again just trying to please clang-tidy.