1

The title says it all.

The first case f(const X&) is the good old fashioned const reference parameter.

However, when taking a mutable reference parameter, why not always use X&&? This way client code can use temps or regular lvalues, rather than f() dictating usage.

EDIT: Sloppiness on my part, sorry. I meant, given fc(const X&) and fm(X&&), do we need fm(X&)?

EDIT: Example: I use stateless decorators to pass as parameters:

class Base64Writer: public Writer {
public:
  Base64Writer(Writer& w): w_(w) {}
private:
  virtual void doWrite() override;
  Writer& w_;
}

void func(Writer&& w) { w.doWrite(); }

// to be called as
Writer wr = ...;
func(Base64Writer(wr));
Benito Ciaro
  • 1,718
  • 12
  • 25
  • What if you don't want temporaries passed? Like after an implicit conversion. – Bo Persson Jan 19 '13 at 09:25
  • The reason for not allowing temporaries with `f(X&)` was that the users were surprised by the bugs it caused. Using `f(X&&)` instead would surely bring those bugs back. – Bo Persson Jan 19 '13 at 09:30

3 Answers3

2
  • If you mean to ask : is it logical to have all three overloads at the same time?

    In most cases, it would be a bad idea. as the semantic of the overloaded functions contradict each other. One promises that it will not modify the argument, the other doesn't make any such promise (it tacitly says it will modify the argument!). The user of such overloaded functions will be confused that differs only by const.

    It makes sense to have these overloads at the same time:

    void process(std::vector<int> const&);
    void process(std::vector<int> &&);
    

    But a third one, in addition to the above two, doesn't make much sense to me:

    void process(std::vector<int> &); //contradicts the first overload
    

    It only adds confusion and makes code harder to read. If you want this semantic, choose a different name for the function!

  • If you mean to ask : which one should you have (though not necessarily all at the same time)?

    I would say, it depends on the semantic. If the parameter behaves as input and/or output parameter, then f(X&) makes sense. If the parameter is only input parameter, then f(X const&) makes sense, in this case you can additionally define f(X&&) also, to gain performance advantage!

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • @BenitoCiaro: though that is not clear in your question, but then my answer also answers that. You don't need to define `f(X&)`, when you already have `f(X&&)`; in most cases it adds confusion. – Nawaz Jan 19 '13 at 09:46
2

Absolutely!

For example, you need it whenever you're manipulating a container passed as an argument.

In fact, you might need all three -- compare these, for example:

vector<int> append_if_not_ends_with(vector<int> const &original, int x)
{
    vector<int> v(original);
    if (v.empty() || v.back() != x)
    { v.push_back(x); }
    return v;
}

vector<int> &append_if_not_ends_with(vector<int> &v, int x)
{
    if (v.empty() || v.back() != x)
    { v.push_back(x); }
    return v;
}

vector<int> append_if_not_ends_with(vector<int> &&original, int x)
{
    vector<int> v(std::move(original));
    if (v.empty() || v.back() != x)
    { v.push_back(x); }
    return v;
}

If you have all 3 overloads, then you can avoid copying the contents of the vector when you don't need to.

vector<int> v1;
vector<int> const &v2 =
    foo ? append_if_not_ends_with(v1) :
    bar ? append_if_not_ends_with(static_cast<vector<int> const &>(v1)) :
          append_if_not_ends_with(vector<int>());
Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886
  • Why would you need BOTH? `std::vector const&` or `std::vector &`? – Nawaz Jan 19 '13 at 09:22
  • @Nawaz: I'm confused, when did I say we would necessarily need both? – user541686 Jan 19 '13 at 09:23
  • So you mean it is logical to have an overloaded function `vector const & append_if_not_ends_with(vector const &v, int x)` ? – Nawaz Jan 19 '13 at 09:23
  • @BenitoCiaro: Does this version answer your question? – user541686 Jan 19 '13 at 09:29
  • @BenitoCiaro: Also be aware of type-lowering rules inside templates -- you can achieve the same thing using only 1 function if the referenced type is a template-dependent type! – user541686 Jan 19 '13 at 09:31
  • @Mehrdad: It only confuses the users. Why not choose a different name for the function which modifies the argument? That surely a better idea, as it increases the readability of the code, almost in all cases. – Nawaz Jan 19 '13 at 09:39
  • @Nawaz: How would you use this function in a template or with perfect forwarding? I mean like `return foo(append_if_not_ends_with(std::forward(arg1)))`. – user541686 Jan 19 '13 at 09:41
  • @BenitoCiaro: You certainly *can* use `move` on an l-value reference, but then the subsequent assignment won't work correctly! (You'd be either making another copy of the vector, or you'd be destroying the original.) The whole point here is to avoid making a copy of the vector's contents. Otherwise you might as well just take in `X` and return `X`, no need for *any* kinds of references whatsoever! – user541686 Jan 19 '13 at 09:42
  • @Mehrdad: It doesn't help perfect forwarding much. If you have ALL three functions in your project, then it is even more difficult to write (and read) template code. – Nawaz Jan 19 '13 at 09:49
  • 1
    @BenitoCiaro: Could you provide an example of what you mean? I'm having trouble seeing how that would avoid copying. – user541686 Jan 19 '13 at 09:53
  • No. What do you mean by "more flexible"? What would it do if the type was movable but not copyable? Vector is just an example here, it doesn't have to be a copyable type like vector. Again, maybe show me an **example** of what you mean if you think I'm wrong, because I don't see how what you're describing would actually work. – user541686 Jan 19 '13 at 10:02
  • @BenitoCiaro: I can't explain it to you if you can't show me an example. – user541686 Jan 19 '13 at 10:32
  • @BenitoCiaro: Thanks for the example, but what is your example demonstrating, exactly? Is it demonstrating the lack of a need for a `Writer&` overload *in that particular scenario*? To me it looks like your code's entire purpose is to avoid using l-value references... is that correct? – user541686 Jan 19 '13 at 10:51
  • The comments are not a good place for long discussions, please move to the chat if you want to continue! – markus Jan 19 '13 at 14:14
1

I am surprised that nobody pointed out that you can actually pass by value, and that this practice in c++11 is actually the right thing to do whenever you think that you need f(X&&).

Imo:

  • Use f(const X &) if you need just to read from the element.
  • Use f(X) if you need to take the ownership of the object passed. The caller of your function can just do f( std::move(something)) or f(something) when appropriate.
  • Use f(X&) if you need to modify the passed object.
sbabbi
  • 11,070
  • 2
  • 29
  • 57
  • @BenitoCiaro Actually I see really few cases in which you would need f(X&&) when you can have f(X), unless your moving constructor is really expensive. See also this question http://stackoverflow.com/questions/7592630/is-pass-by-value-a-reasonable-default-in-c11 – sbabbi Jan 20 '13 at 11:45