1

Common descriptions of the perfect forwarding problem say that we better shouldn't use the combinations of & and const& as wrapper function arguments, because in such case we are obliged to write multiple functions covering all combinations of the function arguments:

template <typename T1, typename T2>
void wrapper(T1& e1, T2& e2)                { func(e1, e2); }

template <typename T1, typename T2>
void wrapper(const T1& e1, T2& e2)          { func(e1, e2); }

template <typename T1, typename T2>
void wrapper(T1& e1, const T2& e2)          { func(e1, e2); }

template <typename T1, typename T2>
void wrapper(const T1& e1, const T2& e2)    { func(e1, e2); }

Here is the classic solution of the problem:

template <typename T1, typename T2>
void wrapper(T1&& e1, T2&& e2) {
    func(forward<T1>(e1), forward<T2>(e2));
}

template<class T>
T&& forward(typename std::remove_reference<T>::type& t) noexcept {
    return static_cast<T&&>(t);
}

But why can't we use const_cast for this purpose? We could write something like:

template <typename T1, typename T2>
void wrapper(const T1& e1, const T2& e2)    
{ 
    T1& e1_ref = const_cast<T1&>(e1);
    T2& e2_ref = const_cast<T2&>(e2);
    func(e1_ref, e2_ref);
}

In this way, we don't have to write multiple functions and we are able to efficiently deal with both lvalues and rvalues. So why do we really need a somewhat tricky solution using reference collapsing, template arguments deduction and std::forward?

undermind
  • 1,779
  • 13
  • 33
  • 4
    What if `func` is overloaded and does different things depending whether its arguments are const or not? In fact, if `func` _doesn't_ do different things depending on its arguments' const-ness and value category, why do you even need to call it with perfect forwarding? – Jonathan Wakely Feb 13 '15 at 11:43
  • 1
    Modifying a const object is UB. So if `func` modifies its argument, you have UB. and if not, you can use `const T&` version without const_cast anyway. – Jarod42 Feb 13 '15 at 11:43
  • 3
    And what if someone actually passed an object declared `const`? – T.C. Feb 13 '15 at 11:43
  • 2
    Also, your solution doesn't preserve the value category of the arguments, i.e. it doesn't forward rvalues as rvalues, everything becomes a non-const lvalue ... that is the most imperfect forwarding I've ever seen. It could be called awful forwarding. – Jonathan Wakely Feb 13 '15 at 11:45
  • 3
    possible duplicate of [Advantages of using forward](http://stackoverflow.com/questions/3582001/advantages-of-using-forward) –  Feb 13 '15 at 11:46
  • The classic solution is to use `std::forward` not to define it yourself, why would you do that? – Jonathan Wakely Feb 13 '15 at 11:57
  • 2
    You're basically asking "what is perfect forwarding used for?", hidden behind a thin veil of "I can do this completely different thing instead if I don't want to use perfect forwarding". :) – Lightness Races in Orbit Feb 13 '15 at 12:01
  • 1
    @JonathanWakely: Might be better called "perfect backwarding". – Lightness Races in Orbit Feb 13 '15 at 12:01
  • @LightnessRacesinOrbit It seems you're right and I should better delve into the heart of the perfect forwarding problem to find the answer. – undermind Feb 13 '15 at 12:08
  • @undermind, read the question remyabel linked to – Jonathan Wakely Feb 13 '15 at 12:10

1 Answers1

9

Your solution is really, really bad.

You modify the const-ness of the arguments, and you don't preserve the value category (i.e. lvalue or rvalue) of the arguments.

Basically your wrapper completely alters the arguments and always calls the same overload of func with no consideration for the const qualifiers and value categories of the original arguments.

In this way, we don't have to write multiple functions and we are able to efficiently deal with both lvalues and rvalues.

You deal with them, but you do it wrong. If your wrapper is called with rvalues it "forwards" them as lvalues. Your program will misbehave if func cares about the distinction between lvalues and rvalues (and if it doesn't, why bother using perfect forwarding at all?) It will also misbehave if func cares about the distinction between const and non-const arguments.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521