Baruch
Baruch

Reputation: 21508

Use of std::forward for non-forwarding references

I am trying to figure out if my use of std::forward in the following code makes sense even though it is not a forwarding (=universal) reference. Please excuse the amount of code, but this is the stripped-down version of what I am trying to achieve.

template <class... Args>
class event_dispatcher {
private:
  using func_t = std::function<bool(Args...)>;

public: 
  bool operator()(Args... args) const {
    for (auto& f : funcs) {
      if (!f(args...))
        return false;
    }
    return true;
  }

  template <class F>
  std::enable_if_t<std::is_invocable_r_v<bool, F, Args...>>
  add(F&& f) {
    funcs.emplace_back(std::forward<F>(f));
  }

  template <class F>
  std::enable_if_t<!std::is_invocable_r_v<bool, F, Args...> && std::is_invocable_v<F, Args...>>
  add(F&& f) {
    add([f_ = std::forward<F>(f)](Args... args){
         std::invoke(f_, std::forward<Args>(args)...); // <-- this is the one I am asking about!
         return true;
       });
  }

private:
  std::vector<func_t> funcs;
};

The idea is that if the callable passed to add() doesn't return a bool, we will wrap it in a lambda that does.

Now, if I just pass args... directly, it will always work, but if any of Args are rvalues then it will needlessly do a copy instead of a move. If I instead use std::move(args)... it will not work if any of Args is an lvalue. Using std::forward<Args>(args)... here seems to solve these problems and work as efficiently as possible in any case, but I am worried that I am missing something since I am using std::forward for a non-forwarding reference, and in general I am having a lot of trouble wrapping my head around the whole reference collapsing / rvalue reference / std::move / std::forward issues.

Upvotes: 3

Views: 571

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275330

std::move does not move std::forward does not forward.

std::forward is a conditional move. std::forward<T> moves if T is a value or rvalue reference.

This lines up with when you want to move args..., so it is appropriate here.

A comment along those lines should be a good idea, as in any situation where you use std::forward outside of simple forwarding references.

Upvotes: 3

Related Questions