Question

I tried the following code:

#include <iostream>

struct test{
    test(){}
    test(test const &){
        std::cout<<__LINE__<<"\n";
    }
    test(test&&){
        std::cout<<__LINE__<<"\n";
    }
};

#include <vector>
#include <utility> // std::move
int main(){
    auto&& tmp = test();
    std::vector<test> v;
    v.push_back(tmp);
    std::cout<<__LINE__<<"\n";
    v.push_back(std::move(tmp));

    return 0;
}

The vs2013 compiler output:

6 // copy

18

9 // move

9 // move

The g++ and clang++ output:

6 // copy

18

9 // move

6 // copy

My problems are:

  1. Is the type of tmp test&&? Is tmp a rvalue?

  2. If the type of tmp is test&&, why didn't the first push_back use move constructor?

  3. Where did the last output come from? Why did vs2013 and g++ output different results?

Thanks.

Answer for 3rd problem: It comes from the reallocation as commented by andrew.punnett.

Was it helpful?

Solution

Is the type of tmp test&&?

Yes and no. tmp is an rvalue reference variable of type test&&, but the identifier tmp taken as an expression has type test and value category lvalue. The & is never part of the type of an expression.

Is tmp a rvalue?

No. Any use of an identifier is an lvalue expression, even the name of an rvalue reference. Rvalue reference variables are accessed essentially identically to lvalue reference variables; only decltype(tmp) can tell the difference. (Often you would use decltype((tmp)) to avoid telling the difference.)

If the type of tmp is test&&, why didn't the first push_back use move constructor?

Because the name of an rvalue reference is still an lvalue. To get an rvalue expression, use move(tmp).

Where did the last output come from? Why did vs2013 and g++ output different results?

Clang and GCC only made room for one object in the vector by default. When you added the second object, the vector storage was reallocated causing the objects to be copied. Why weren't they moved? Because the move constructor is not noexcept, so if it threw an exception, it would be impossible to undo the reallocation.

As for the two moves by MSVC, there are two possibilities, which you can distinguish by experimentation — I don't have a copy handy.

  1. MSVC reserved enough space inside the vector for two objects by default. The second move is from an internal local variable.
  2. MSVC ignored the requirement that the move constructor be noexcept and called it to perform relocation anyway. This would be a bug, but in this case it's covering up a common mistake.

If you replace vector with deque, you won't see any more copies, because deque is not allowed to assume copyability.

OTHER TIPS

Visual Studio do not yet support the noexcept keyword and is likely not compliant to the exception safety of push_back. Also, the additional output is a result of the difference on capacity computation on grow.

#include <iostream>
#include <vector>
#include <utility> // std::move

struct Except{
    Except(){}
    Except(Except const &) {
        std::cout<< "COPY\n";
    }
    Except(Except&&)  {
        std::cout<< "MOVE\n";
    }
};

struct NoExcept{
    NoExcept(){}
    NoExcept(NoExcept const &) noexcept {
        std::cout<< "COPY\n";
    }
    NoExcept(NoExcept&&) noexcept {
        std::cout<< "MOVE\n";
    }
};

template <typename T> void Test( char const *title,int reserve = 0) {
    auto&& tmp = T();
    std::cout<< title <<"\n";
    std::vector<T> v;
    v.reserve(reserve);

    std::cout<< "LVALUE REF ";
    v.push_back(tmp);
    std::cout<< "RVALUE REF ";
    v.push_back(std::move(tmp));
    std::cout<< "---\n\n";
}
int main(){
    Test<Except>( "Except class without reserve" );
    Test<Except>( "Except class with reserve", 10 );
    Test<NoExcept>( "NoExcept class without reserve" );
    Test<NoExcept>( "NoExcept class with reserve", 10 );
}

And the result within clang :

Except class without reserve
LVALUE REF COPY
RVALUE REF MOVE
COPY
---

Except class with reserve
LVALUE REF COPY
RVALUE REF MOVE
---

NoExcept class without reserve
LVALUE REF COPY
RVALUE REF MOVE
MOVE
---

NoExcept class with reserve
LVALUE REF COPY
RVALUE REF MOVE
---
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top