#include #include c" /> #include #include c" /> #include #include c"/>

How does c++ choose move and copy construct

151 Views Asked by At

I'm working out an "Any" class by myself. As following code shown, I have two questions.

#include <assert.h>
#include <iostream>
#include <typeinfo>

class Test{};

class Any {
 public:
  template<typename DataType>
  explicit Any(DataType&& in) {
    Test t;
    std::cout
        << typeid(t).name() << "  "
        << typeid(in).name() << "  "
        << typeid(Test()).name();
    std::cout << " move";
  }
  template<typename DataType>
  explicit Any(const DataType& in) {
    Test t;
    std::cout
        << typeid(t).name() << "  "
        << typeid(in).name() << "  "
        << typeid(Test()).name();
    std::cout << " copy";
  }
};

int main()
{
  Test t;
  Any a(t);
}

Compilation command is

g++ main.cpp -std=c++11

The output is

4Test  4Test  F4TestvE move
  1. Why does c++ choose move construct rather than copy construct? "t" is an instance of Test which it's not a rvalue.
  2. Why typeid(in) and typeid(Test()) is not same? They are both rvalue.

Thanks a lot.

2

There are 2 best solutions below

0
leslie.yao On
  1. Why does c++ choose move construct rather than copy construct? "t" is an instance of Test which it's not a rvalue.

The 1st constructor overload takes forwarding reference and could accept both lvalues and rvalues. (Hence it's not move constructor.) For Any a(t); it's an exact match, while for the 2nd overload t needs to be converted to const.

  1. Why typeid(in) and typeid(Test()) is not same? They are both rvalue.

Test() is a function type, which returns Test and takes nothing, thus typeid(Test()) gives different result.

0
bitmask On

The reason your move variant is invoked is because its signature doesn't actually require an rvalue reference to be passed.

If U = T const& then U&& will be a T const&, not a T&&. In your case DataType binds to Test&, not, as you likely expected, to Test. The && therefore collapses and does nothing. You can see this in action if you add a static_assert(std::is_same_v<DataType, Test&>); which will pass.
You function will be called if you replace the template with a fixed Type, i.e. Test&& instead of DataType&&.

Have a look at reference, section Reference collapsing.