When to use rvalue reference
Asked Answered
S

1

5

So basically my question is: When should I use an rvalue reference? In this example, I'm working on a logger class (it just logs things to the console...). I have different functions to log messages on different log levels. They take in an std::string as a parameter. Should I have two versions of each function, the first one for a "normal" reference and the second for an rvalue reference?

namespace lnr
{
    class logger
    {
    public:
        logger(const string& name);
        logger(const string&& name);
        ~logger() = default;

        const void trace(const string& message) const;
        const void trace(const string&& message) const;
        const void info(const string& message) const;
        const void info(const string&& message) const;
        const void warn(const string& message) const;
        const void warn(const string&& message) const;
        const void error(const string& message) const;
        const void error(const string&& message) const;


    private:
        const string name;
    };
}

Because it is quiete common to log something with both

logger.trace("Hello");

and

std::string error_message = "...";
logger.error(error_message);

But having every function two times is really weird and it also seems a bit unnecessary...

Sync answered 3/6, 2020 at 14:48 Comment(2)
there is no need to rvalue catch a const parameter, just treat it as const lvaue ref and everything still good.Flanigan
rvalue const reference has practically no use cases, because a const ref does the same thing. instead, rvalue are passed by non-const reference, and so you can move them , and for example, using vector, moving a vector means something like "swapping a pointer", where copying it means copy every elementUsa
S
7

If you're taking in a string that you're going to copy and store, you're better off not taking references at all: take a value.

struct T
{
    T(std::string str) : m_str(std::move(str)) {}

private:
    std::string m_str;
};

Now, the person making the T (hah!) can either just pass in an existing string that'll get copied without affecting the original string… or they can std::move a string they don't need any more.

So, if you need a copy, you just get one copy; if you don't, you just get a string of moves. Nice and cheap!


If you're taking in a string just to use in that function, i.e. you're not taking ownership, there's no reason to bother about any of this. Just accept a nice old-fashioned const std::string&.

void foo(const std::string& str)
{
   std::cout << str << '\n';
}

Though there are some rare exceptions, I generally only have a function take an rvalue reference when it's a move constructor or a move assignment operator. We do that because the semantics of the language's "overload resolution" rules are built around moves working that way (and because having a possible copy, like in my first example, is not desirable when we have no "target" object to put it in).

You will also see things like auto&&, or T&& where T is a template parameter. This is not actually an rvalue reference, but a so-called forwarding reference, which has uses when you want to pass along the rvalue-ness of an argument where possible, depending on the template context.

Salita answered 3/6, 2020 at 14:53 Comment(4)
Is it worth taking str by value when we could decide that we no longer want to store str, but use it instead.Close
@Close If you change your mind, that's fine, change your mind; turn it into a reference.Salita
And if you're going to say "but then you have to change the interface" - well, that's fine. If the semantics of your function have changed that much, the users of it should be reviewing their uses of it anyway. Ideally you would instead create a new function with a different name to avoid regressions. Don't change the meaning of functions. Mark the "old" ones deprecated, and eventually remove them.Salita
I see what you're saying, and in general I would pass by value if I need to store the input, or use const & otherwise. But if this class is part of an interface to my library, I would prefer to provide constructors for both const std::string & and std::string && to avoid future interface changes. If I didn't need to store the input I would only provide const std::string & and add the rvalue reference later if it becomes necessary.Close

© 2022 - 2024 — McMap. All rights reserved.