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?
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.