Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::forward and copy constructor

I have started leaning C++ recently. And I have a question on std::forward with lvalue reference and rvalue reference. In my understanding, The Func(mc) in the following code suppose to call Func(T& t) because of template parameter deduction rule. And copy constructor of MyClass should be called inside the function with a message, "copy constructor". However I cannot get it when I run the program. I checked that std::forwad with rvalue reference and copy constructor is working well in other lines. Please help me to understand what happen in the code. If I make easy mistake or misunderstanding, I am sorry for taking your time. Thank you very much.

class MyClass {
public:
    MyClass() { printf("constructor.\n"); };
    MyClass(MyClass&) { printf("copy constructor.\n"); };
    MyClass(const MyClass&) { printf("const copy constructor.\n"); };
    MyClass(MyClass&&) { printf("move constructor.\n"); };
    int val = 3;
};

template <typename T>
void Func(T&& t) {
    T new_t_(std::forward<T>(t));
    new_t_.val *= 2;
};

main() {
    MyClass mc;
    Func(mc);  // lvalue <- Func(T&)
    Func(MyClass());  // rsvalue <- Func(T&&)
    printf("mc.val=%d\n", mc.val); // this is for check

    MyClass mc2(mc);  // this is for check of copy constructor
}

Output when I run program is following,

constructor.
constructor.
move constructor.
mc.val=6
copy constructor.

I think it should have "copy constructor" between the first and the second "constructor" messages.

Thank you very much again.

like image 304
mora Avatar asked May 19 '26 15:05

mora


1 Answers

Func(mc);

This call will deduce T to be MyClass&. Note the reference. This is due to the way perfect forwarding has been introduced into C++: A forwarding reference such as the T&& function parameter of Func will be deduced to be an lvalue reference if the corresponding function argument is an lvalue-expression; otherwise (for xvalues and prvalues) it will not be deduced to be a reference type.

The type deduced for T will then be reference-collapsed with the && from the function parameter T&& t to form the final function parameter type. In the case Func(mc), T == MyClass&, so the function parameter becomes MyClass& &&, collapsed to MyClass&.

Since T == MyClass&, the declaration of the local variable new_t_ will declare a reference in this case. No new object will be created:

template <>
void Func<MyClass&>(MyClass& t) {
    MyClass& new_t_(std::forward<MyClass&>(t));
    new_t_.val *= 2;
}

Func(MyClass());

Here, the function argument is the prvalue-expression MyClass(). T will be deduced to MyClass (without reference). The function template instantiation produced looks like this:

template <>
void Func<MyClass>(MyClass&& t) {
    MyClass new_t_(std::forward<MyClass>(t));
    new_t_.val *= 2;
}

If you want to copy/move the function argument into a local variable, the std::decay metafunction can be used - or, more specific to the OP's problem, the std::remove_reference metafunction. E.g.

template <typename T>
void Func(T&& t) {
    using DT = typename std::decay<T>::type;
    DT new_t_(std::forward<T>(t));
    new_t_.val *= 2;
}
like image 70
dyp Avatar answered May 22 '26 03:05

dyp



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!