Reputation: 12963
I have this function:
fstream open_user_file() const
{
...
}
but my compiler complains about fstream
copy-constructor being implicitly deleted. Given that the compiler performs RVO, why is the copy constructor chosen instead of the move constructor?
Otherwise, what's the best way to do this?
Upvotes: 4
Views: 1312
Reputation: 219185
The currently accepted answer is just wrong.
When returning a local variable with automatic storage, of the same type as the declared return type of the function, then there is a two phase process going on:
fstream open_user_file() const
{
fstream f;
/*...*/
return f;
}
The selection of the constructor for the copy is first performed as if the object were designated by an rvalue.
If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.
This means that if f
is move constructible, that will be preferred (and possibly elided) for returning f
. Else if f
is copy constructible that will be done (and possibly elided) for returning f
. Otherwise f
can not be returned from this function and a compile-time error should result.
The only case in which:
return std::move(f);
should help is when the implementation is buggy. In a conforming implementation, fstream
is move constructible and:
return f;
will be optimal. If f
is not move constructible, then:
return std::move(f);
won't help in a conforming implementation. And if coded anyway in a conforming implementation will have the effect of a pessimization, in that it will inhibit RVO.
gcc 4.8 has not implemented movable streams (and streams are not copyable). And this is the source of your problem. In C++98, C++03, and gcc 4.8, streams are not returnable from functions. In C++11 they are.
Upvotes: 10
Reputation:
An implementation may omit a copy operation resulting from a return statement, even if the copy constructor has side effects. In this case you may just have to explicitly move.
fstream open_user_file() const
{
fstream f;
/*...*/
return std::move(f);
}
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects.
...
And this is where it says the copy constructor must be accessible:
When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided.
Upvotes: 3