When to use rvalue reference

Viewed 262

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...

1 Answers

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.

Related