Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

F.16 (pass by reference to const / by value): Request for clarification #2170

Closed
siebenschlaefer opened this issue Jan 17, 2024 · 6 comments · Fixed by #2172
Closed

F.16 (pass by reference to const / by value): Request for clarification #2170

siebenschlaefer opened this issue Jan 17, 2024 · 6 comments · Fixed by #2172
Assignees

Comments

@siebenschlaefer
Copy link

siebenschlaefer commented Jan 17, 2024

I often write functions that take an object that is expensive to copy but they need a "working copy" that they can modify.

I would like to take the argument by value and then modify the parameter inside the function.
That is simple, and it handles both lvalue and rvalue arguments efficiently.
Simple examples include a function that takes a std::string, modifies and returns its. Or a function that takes a square matrix, transforms it into the diagonal matrix, uses that in a calculation, but does not want to modify the original matrix.

But F.16 seems to advise against it, neither the "advanced uses" nor the exception cover this use case.
They seem to suggest ...

  • either taking the argument by reference to const, and creating a copy that the function then can modify.
    But that's less efficient if the user passes an rvalue to the function because the function has to create a separate copy, and it's little bit more code and a tiny bit more complicated code.
  • Or (in the paragraph about "advanced uses") adding an overload that takes an rvalue reference.
    But that's more complex than a single function, there are now two functions to write and to maintain, and instead of a single function I now have an overload set.

What am I missing or misunderstanding? Is my use case so exotic? Is my analysis wrong? Is my interpretation of F.16 wrong?

@iglesias
Copy link
Contributor

Hi, I can't sure what you are missing or misunderstanding, although your explanations dealing with rvalue (reference(s)) are not clear to me. This also pertains your third and fourth questions.

I don't think your use case is so exotic and to comply with the guideline as it is I'd follow the approach in your first bullet point: I think that, if I am understanding you correctly,

auto f(T const& t) {
  // This would be what you called little bit more code, correct? Strictly, with the const& too.
  auto t_copy = t;
  ...
}

is not in fact "tiny bit more"(?) complicated code than

auto f(T t) {
  ...
}

@beinhaerter
Copy link
Contributor

beinhaerter commented Jan 18, 2024

@iglesias siebenschlaefer said that the const& code is less efficient when you pass an rvalue to it. You can compare it using the code below.

I agree with @siebenschlaefer and that is why I am using this pass-by-value approach in my code, too. Contradicting 'F.18: For “will-move-from” parameters, pass by X&& and std::move the parameter' I even use that for will-move-from parameters to give the caller of my function more freedom and to not require him to std::move().

#include <iostream>

struct S {
    S() { std::cout << "ctor\n"; }
    S(const S&) { std::cout << "copy ctor\n"; }
    S(S&&) { std::cout << "move ctor\n"; }
};

S get() noexcept { return S{}; }

void fc(const S& s) { S copy{s}; }

void fr(S&& s) {}

void fv(S s) {}

int main() {
    S s;
    std::cout << "\nf taking const&, getting lvalue\n";
    fc(s);
    std::cout << "\nf taking const&, getting rvalue\n";
    fc(get());
    std::cout << "\n\nf taking rvalue\n";
    fr(get());
    std::cout << "\n\nf taking value, getting lvalue\n";
    fv(s);
    std::cout << "\nf taking value, getting rvalue\n";
    fv(get());
}

@iglesias
Copy link
Contributor

Hi @beinhaerter, may I ask you double-check the snippet, please? I think that the lvalue in the last std::cout should be rvalue.

Disregarding that and assuming the snippet's point is correct. I feel that better measurement is needed to reach the conclusion that it is less efficient.

@beinhaerter
Copy link
Contributor

Hi @beinhaerter, may I ask you double-check the snippet, please? I think that the lvalue in the last std::cout should be rvalue.

Thanks for pointing that out. I corrected the code snippet.

@siebenschlaefer
Copy link
Author

@iglesias

[...] I'd follow the approach in your first bullet point [...]

Taking the argument by reference to const and then creating a copy is less efficient
if the caller passes an rvalue to the function
because that creates copy, whereas taking the argument by value would move.

@hsutter
Copy link
Contributor

hsutter commented Jan 18, 2024

Editors call: Thanks! We intend to update the wording to cover this case better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants