5

In the following code, what is the advantage of using &&? The code is from answer at Specialize same operator for different traits

From this question, I get that an && argument means it is a reference that can be modified by the function.

The decay_t probably prevents the compiler to interpret a reference to a variable as an array, as in What is std::decay and when it should be used?

std::forward is perfect forwarding as described here. Why do we need this forwarding?

Thanks.

#include <iostream>
#include <type_traits>
#include<utility>

class A;

template <typename T>
struct is_A : std::false_type {};
template <> struct is_A<A> : std::true_type {};

template <typename T>
struct is_int : std::false_type {};
template <> struct is_int<int> : std::true_type {};
template <> struct is_int<long> : std::true_type {};

class A{
public:
    int val;

    void print(void){
        std::cout << val << std::endl;
    }

    template <typename T1>
    std::enable_if_t<is_int<std::decay_t<T1>>::value, void>
    operator=(T1 && input){
        val = 2*std::forward<T1>(input);
    }

    template <typename T1>
    std::enable_if_t<is_A<std::decay_t<T1>>::value,void>
    operator=(T1 && Bb){
        val = 5*std::forward<T1>(Bb).val;
    }
};

int main(void){
    A Aa;
    A Bb;
    int in_a = 3;
    Aa = in_a;
    Bb = Aa;
    Aa.print(); //This should give 6. (3x2)
    Bb.print(); //This should give 30. (6x5)
}
Community
  • 1
  • 1
rxu
  • 1,369
  • 1
  • 11
  • 29
  • 2
    The `&&` in this code are semi-formally referred to as forwarding references. They bind to both lvalues and rvalues, and the `T1` passed to `std::forward` contains the information on whether the object was originally an lvalue or an rvalue. In short, the "advantage" of `&&` is that it allows perfect forwarding. – KABoissonneault Sep 16 '16 at 15:24
  • 1
    So the whole thing is to allow perfect forwarding... Does this also avoid creating any temporary copy of any part of the `T1 && Bb` argument? – rxu Sep 16 '16 at 15:30
  • 1
    @rxu: It won't create a copy unless the function (or operator overload) it was forwarded to receives the argument by value (or by forwarding reference, then itself uses it in a pass-by-value way). – ShadowRanger Sep 16 '16 at 15:48
  • 1
    With `T1&&` you can also write `Aa = 3;` in `main`, but this wouldn't work with `T1&`. – alain Sep 16 '16 at 15:51

1 Answers1

3

Actually, it's a (let me say) trick so as the example code works.
In fact, the standard says that:

A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X& or const volatileX&.

Moreover:

If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. 

Now, try to copy-assign a const reference to the variable, as an example:

Bb = static_cast<const A&>(Aa);

The result will be no longer the one expected.

Anyway, as long as you deal only with non cv-qualified lvalue/rvalue references, the forwarding reference used in the answer works fine, as shown in the example code.
It intercepts a bunch of specific types and does its work. That's all.

As mentioned in the comments by @Jarod42:

Note that you still have to write the operator=(const A&) to handle it as it is special.

Not so special indeed. You don't have a proper copy assignment operator in that code if you don't define it.
As said in the comments, to define it

[...] is left as an exercise to the reader. :-)

So, what's the advantage of doing that?
A shortcut to offer a minimal, working solution. Not a complete, production-ready piece of code, of course.

skypjack
  • 49,335
  • 19
  • 95
  • 187