jeudi 31 août 2017

Forwarding reference vs const lvalue reference in template code

I've recently been looking into forwarding references in C++ and below is a quick summary of my current understanding of the concept.

Let's say I have a template function footaking a forwarding reference to a single argument of type T.

template<typename T>
void foo(T&& arg);

If I call this function with an lvalue then T will be deduced as T& making the arg parameter be of type T& due to the reference collapsing rules T& && -> T&.

If this function gets called with an unnamed temporary, such as the result of a function call, then Twill be deduced as T making the arg parameter be of type T&&.

Inside foo however, arg is a named parameter so I will need to use std::forward if I want to pass the parameter along to other functions and still maintain its value category.

template<typename T>
void foo(T&& arg)
{
    bar(std::forward<T>(arg));
}

As far as I understand the cv-qualifiers are unaffected by this forwarding. This means that if I call foo with a named const variable then T will be deduced as const T& and hence the type of arg will also be const T& due to the reference collapsing rules. For const rvalues T will be deduced as const T and hence arg will be of type const T&&.

This also means that if I modify the value of arg inside foo I will get a compile time error if I did infact pass a const variable to it.

Now onto my question. Assume I am writing a container class and want to provide a method for inserting objects into my container.

template<typename T>
class Container
{
public:
    void insert(T&& obj) { storage[size++] = std::forward<T>(obj); }
private:
    T *storage;
    std::size_t size;
    /* ... */
};

By making the insert member function take a forwarding reference to obj I can use std::forward to take advantage of the move assignment operator of the stored type T if insert was infact passed a temporary object.

Previously, when I didn't know anything about forwarding references I would have written this member function taking a const lvalue reference: void insert(const T& obj).

The downside of this is that this code does not take advantage of the (presumably more efficient) move assignment operator if insert was passed a temporary object.

Assuming I haven't missed anything.

Is there any reason to provide two overloads for the insert function? One taking a const lvalue reference and one taking a forwarding reference.

void insert(const T& obj);
void insert(T&& obj);

The reason I'm asking is that the reference documentation for std::vectorstates that the push_back method comes in two overloads.

void push_back (const value_type& val);
void push_back (value_type&& val);

Why is the first version (taking a const value_type&) needed?

Aucun commentaire:

Enregistrer un commentaire