Curious
Curious

Reputation: 21560

std::forward and ref-qualifiers for member functions

I was in a position where I was using std::forward wherever I had a forwarding reference and I was wondering if some of that was unnecessary or even wrong. For example having std::forward in a std::begin() call.

Because of the ability of a class being able to overload its member functions based on whether the call is made with the object as an rvalue or not https://akrzemi1.wordpress.com/2014/06/02/ref-qualifiers/, I assumed that a templated function would be as efficient as possible if you were knew that the inner function you were forwarding the object to was non-mutating and/or was a member function of the object. For example

template <typename Something>
void do_something(Something&& something) {
    std::forward<Something>(something).do_something();
    auto iter = std::begin(std::forward<Something>(something));
    for_each(iter, std::end(std::forward<Something>(something), []() {});
}

I saw this question (When not to use std::forward with r-values?) but it did not address the member function ref-qualifiers and it also did not clearly address what the best practice is when you don't have access to the inner functions definitions that you are calling.

Is there a general guideline for when not to use std::forward that addresses the things I mentioned? Or is there some key concept that I am missing?

Upvotes: 1

Views: 305

Answers (2)

Tomilov Anatoliy
Tomilov Anatoliy

Reputation: 16741

The problem is a use after object moved from ("stealed"). If something used in a multiple lines of do_something, then use std::forward only at the last such line to prevent the problem.

Upvotes: 0

Jarod42
Jarod42

Reputation: 218278

Except if you know the type you will have, avoid to use std::forward several time in the same function for the same object, as the second time, your object might be moved.

// assuming implementation which really need pass by value instead of const reference
template <typename T> void pass_by_value(T t) { std::cout << t; }

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

a call of foo with std::string

foo(std::string("Hello world")); // or "Hello world"s

might call the equivalent to

    pass_by_value(std::string("Hello world"));
    pass_by_value(std::string("")); // Moved string...

Upvotes: 1

Related Questions