Search code examples
c++movestd-pair

std::pair from temporary: Why is copy constructor called and not move constructor?


Consider the following code:

#include<iostream>
using namespace std;
struct A {
    A(const A&){
        cout<<"copy constructor"<<endl;
    }
    A(A&&){
        cout<<"move constructor"<<endl;
    }
    ~A(){
        cout<<"destructor"<<endl;
    }
    static std::pair<A,int> f1()
    {
        int i = 1;
        return std::pair<A,int>{i,2};
    }
    static std::pair<A,int> f2()
    {
        int i = 1;
        return std::pair<A,int>{A(i),2};
    }
    private:
    A(int){
        cout<<"constructor"<<endl;
    }
};

int main()
{
    cout<<"f1: "<<endl;
    A::f1();
    cout<<"f2: "<<endl;
    A::f2();
}

The constructor A(int) is private, therefore the A in the pair<A,int> cannot be in place constructed from an int. Hence, a temporary is constructed in f1. In f2 I create the temporary explicitly but the behavior is different, the output is:

f1: 
constructor
copy constructor
destructor
destructor
f2: 
constructor
move constructor
destructor
destructor

I would expect the move constructor to be called also in A::f1 but the copy constructor is called which is sub optimal. Why is this the case?


Solution

  • If you look at pair's constructors

    Interesting constructors are (2) and (3)

    // (2)
    constexpr pair( const T1& x, const T2& y );
    
    // (3)
    template< class U1 = T1, class U2 = T2 >
    constexpr pair( U1&& x, U2&& y ); // SFINAE on constructible
    

    Notice, there are no pair(T1&&, T2&&).

    As A(int) is private, std::is_constructible_v<A, int> is false.

    So for f1, only (2) is a viable constructor (so the copy).

    For f2, (3) is viable (and better match), so a forward (so a move) is done.