Returning an fstream - c++

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?

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.

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.

Related

std::optional: Not participating in overload resolution vs. being defined as deleted

I am trying to understand the mechanism behind type traits propagation as described for std::optional in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0602r4.html. There is a subtle difference in the treatment of copy operations, which shall be conditionally defined as deleted, versus move operations, which shall rather not participate in overload resolution.
What is the reason for that difference, and how would I test the latter? Example:
#include <type_traits>
#include <optional>
struct NonMoveable {
NonMoveable() = default;
NonMoveable(NonMoveable const&) = default;
NonMoveable(NonMoveable&&) = delete;
NonMoveable& operator=(NonMoveable const&) = default;
NonMoveable& operator=(NonMoveable&&) = delete;
};
// Inner traits as expected
static_assert(!std::is_move_constructible<NonMoveable>::value);
static_assert(!std::is_move_assignable<NonMoveable>::value);
// The wrapper is moveable, via copy operations participating in
// overload resolution. How to verify that the move operations don't?
static_assert(std::is_move_constructible<std::optional<NonMoveable>>::value);
static_assert(std::is_move_assignable<std::optional<NonMoveable>>::value);
int main(int argc, char* argv[])
{
NonMoveable a1;
NonMoveable a2{std::move(a1)}; // Bad, as expected
std::optional<NonMoveable> b1;
std::optional<NonMoveable> b2{std::move(b1)}; // Good, see above. But
// useless as a test for
// P0602R4.
return 0;
}
Bonus Question
Does GCC do the right thing? I have modified the example a bit to get a tiny step closer: https://godbolt.org/z/br1vx1. Here I made the copy operations inaccessible by declaring them private. GCC-10.2 with -std=c++20 now fails the static asserts and complains
error: use of deleted function 'std::optional<NonMoveable>::optional(std::optional<NonMoveable>&&)'
According to Why do C++11-deleted functions participate in overload resolution? the delete is applied after overload resolution, which could indicate that the move constructor participated, despite P0602R4 said it shall not.
On the other hand https://en.cppreference.com/w/cpp/language/overload_resolution states right in the beginning
... If these steps produce more than one candidate function, then overload resolution is performed ...
so overload resolution was skipped, because the move constructor was the only candidate?
std::optional is a red herring; the key is understanding the mechanisms that lead to why these requirements are placed on a library type
There is a subtle difference in the treatment of copy operations, which shall be conditionally defined as deleted, versus move operations, which shall rather not participate in overload resolution.
The under-the-hood requirements (and how to implement these) for std::optional are complex. However, the requirement that move operations shall not participate in overload resolution (for non-movable types) vs copy operations being deleted (for non-copyable types) likely relates to a separate topic;
NRVO(1) (a case of copy elision, if you may) and
more implicit moves, e.g. choosing move constructors over copy constructors when returning named objects with automatic storage duration from a function.
We can understand this topic by looking at simpler types than std::optional.
(1) Named Returned Value Optimization
TLDR
The move eagerness that is expanding in C++ (more eager moves in C++20) means there are special cases where a move constructor will be chosen over a copy constructor even if the move constructor has been deleted. The only way to avoid these for, say, non-movable types, is to make sure the type has no move constructor nor a move assignment operator, by knowledge of the rule of 5 and what governs whether these are defined implicitly.
The same preference does not exist for copying, and there would be no reasons to favour removing these over deleting them, if this was even possible (2). In other words, the same kinks that exist for favouring moves in overload resolution, that sometimes unexpectedly choose move over copy, is not present for the reverse; copy over move.
(2) There is no such thing as a class without the existence of a copy ctor and a copy assignment operator (although these may be defined as deleted).
Implicit move eagerness
Consider the following types:
struct A {
A() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
A(A const &) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
A &operator=(A const &) {
std::cout << __PRETTY_FUNCTION__ << "\n";
return *this;
}
};
struct B {
B() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
B(B const &) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
B &operator=(B const &) {
std::cout << __PRETTY_FUNCTION__ << "\n";
return *this;
}
B(B &&) = delete;
B &operator=(B &&) = delete;
};
Where, A:
has user-defined constructors, such that a move constructor and move assigment operator will not be implicitly defined,
and where B, moreover:
declares and deletes a move constructor and a move assignment operator; as these are declared and defined as deleted, they will participate in overload resolution.
Before we continue through different standard versions, we define the following functions that we shall return to:
A getA() {
A a{};
return a;
}
B getB() {
B b{};
return b;
}
C++14
Now, in C++14 an implementation was allowed to implement copy(/move) elision for certain scenarios; citing [class.copy]/31 from N4140 (C++14 + editorial fixes) [emphasis mine]:
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. [...]
This elision of copy/move operations, called copy elision, is permitted in the following circumstances:
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value
[...]
and, from [class.copy]/32 [emphasis mine]:
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
But [class.temporary]/1 still placed the same semantic restrictions on an elided copy of an object as if the copy had actually not been elided [emphasis mine]
[..] Even when the creation of the temporary object is unevaluated (Clause [expr]) or otherwise avoided ([class.copy]), all the semantic restrictions shall be respected as if the temporary object had been created and later destroyed.
Such that, even for a situation where copy elision was eligible (and performed), the conversion sequences from, say, a named object viable for NRVO, would need to go through overload resolution to find (possibly elided) converting constructors, and would start with a pass through overload resolution as if the object were designated by an rvalue. This means, that in C++14, the following was well-formed
auto aa{getA()}; // OK, and copy most likely elided.
whereas the following was ill-formed:
auto bb{getB()}; // error: use of deleted function 'B::B(B&&)'
as overload resolution would find the declared but deleted move constructor of B during the step of considering b in return b; in getB() as an rvalue. For A, no move constructor exists, meaning overload resolution for a in return a; in getA() with a as an rvalue would fail, and whereafter overload resolution without this kink would succeed in finding the copy constructor of A (which would subsequently be elided).
C++17
Now, in C++17 copy elision was made stronger by the concept of delayed (end entirely elided) materialization of temporaries, particularly adding [class.temporary]/3 [emphasis mine]:
When an object of class type X is passed to or returned from a function, if each copy constructor, move constructor, and destructor of X is either trivial or deleted, and X has at least one non-deleted copy or move constructor, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function's parameter or return object is initialized as if by using the non-deleted trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object).
This makes a large difference, as copy elision can now be performed for getB() without passing through the special rules of return value overload resolution (which previously picked the deleted move constructor), such that both of these are well-formed in C++17:
auto aa(getA()); // OK, copy elided.
auto bb(getB()); // OK, copy elided.
C++20
C++20 implements P1825R0 which allows even more implicit moves, expanding the cases where move construction or assignment may take place even when one would, at first glance, expect a copy construction/assignment (possible elided).
Summary
The quite complex rules with regard to move eagerness (over copying) can have some unexpected effects, and if a designer wants to make sure a type will not run into a corner case where a deleted move constructor or move assignment operator takes precedence in overload resolution over an non-deleted copy constructor or copy assignment operator, it is better to make sure that there are no move ctor/assignment operator available for overload resolution to find (for these cases), as compared to declaring them and defining them as explicitly-deleted. This argument does not apply for the move ctor/copy assignment operator however, as:
the standard contains no similar copy-eagerness (over move), and
there is no such thing as a class without a copy constructor or copy assignment operator, and removing these from overload resolution is basically(3) only possible in C++20 using requires-clauses.
As an example (and probably a GCC regression bug) of the difficulty of getting these rules right for a non-language lawyer, GCC trunk currently rejects the following program for C++20 (DEMO):
// B as above
B getB() {
B b{};
return b;
}
with the error message
error: use of deleted function 'B::B(B&&)'
In this case, one would expect a copy (possibly elided) to be chosen above in case B had deleted its move ctor. When in doubt, make sure the move ctor and assignment operator don't participate (i.e., exist) in overload resolution.
(3) One could declare a deleted assignment operator overloaded with both const- and ref-qualifiers, say const A& operator=(const A&) const && = delete;, which would very seldom be a viable candidate during overload solution (assignment to const rvalue), and which would guarantee the non-existence of the other non-const and &-qualified overloads that would otherwise likely to be valid overload candidates.

How to do type conversion on an "implicit" rvalue in the return statement

Is this invalid? gcc accepts it, clang and msvc don't.
#include <memory>
class Holder {
std::unique_ptr<int> data;
public:
operator std::unique_ptr<int>() && { return std::move(data); }
};
std::unique_ptr<int> test()
{
Holder val;
return val;
}
Assuming that I don't want to add something like std::unique_ptr<int> Holder::TakeData() { return std::move(data); }, the only other workaround I could think of is moving in the return value:
std::unique_ptr<int> test()
{
Holder val;
return std::move(val); // lets the conversion proceed
}
But then gcc 9.3+ has the gall to tell me that the std::move is redundant (with all warnings enabled). WTF? I mean yeah, gcc doesn't need the move, sure, but nothing else accepts the code then. And if it won't be gcc, then some humans inevitably will balk at it later.
What's the authoritative last word on whether it should compile as-is or not?
How should such code be written? Should I put in this seemingly noisy TakeData function and use it? Worse yet - should I maybe make the TakeData function limited to rvalue context, i.e. having to do return std::move(val).TakeData() ?
Adding operator std::unique_ptr<int>() & { return std::move(data); } is not an option, since it obviously leads to nasty bugs - it can be invoked in wrong context.
The "implicit" rvalue conversion is standard mandated. But depending on which standard version you are using, which compiler is "correct" varies.
In C++17
[class.copy.elision] (emphasis mine)
3 In the following copy-initialization contexts, a move operation
might be used instead of a copy operation:
If the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic
storage duration declared in the body or parameter-declaration-clause
of the innermost enclosing function or lambda-expression, or
...
overload resolution to select 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. [ 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.  — end note ]
Up to C++17, GCC is wrong. Using val implicitly as an rvalue should fail to initialize the return type on account of the sentence I marked in bold (the rvalue reference in the unique_ptr c'tor doesn't bind directly to val). But come C++20, that sentence is no longer there.
C++20
3 An implicitly movable entity is a variable of automatic storage
duration that is either a non-volatile object or an rvalue reference
to a non-volatile object type. In the following copy-initialization
contexts, a move operation might be used instead of a copy operation:
If the expression in a return ([stmt.return]) or co_­return ([stmt.return.coroutine]) statement is a (possibly parenthesized)
id-expression that names an implicitly movable entity declared in the
body or parameter-declaration-clause of the innermost enclosing
function or lambda-expression, or
[...]
overload resolution to select the constructor for the copy or the
return_­value overload to call is first performed as if the expression
or operand were an rvalue. If the first overload resolution fails or
was not performed, overload resolution is performed again, considering
the expression or operand as an lvalue. [ Note: This two-stage
overload resolution must be performed regardless of whether copy
elision will occur. It determines the constructor or the return_­value
overload to be called if elision is not performed, and the selected
constructor or return_­value overload must be accessible even if the
call is elided. — end note ]
The correctness of the code is thus subject to the time travel properties of your compiler(s).
As far as how should code like that should be written. If you aren't getting consistent results, an option would be to use the exact return type of the function
std::unique_ptr<int> test()
{
Holder val;
std::unique_ptr<int> ret_val = std::move(val);
return ret_val;
}
I agree from the get go that this may not look as appealing as simply returning val, but at least it plays nice with NRVO. So we aren't likely to get more copies of unique_ptr than we desired originally.
If that is too unappealing still, then I find your idea of a resource stealing member function to be most to my liking. But no accounting for taste.

Constructor called on return statement

Consider the following example:
class X {
public:
X() = default;
X(const X&) = default;
X(X&&) = delete;
};
X foo() {
X result;
return result;
}
int main() {
foo();
}
Clang and GCC disagree on whether this program is valid. GCC tries to call the move constructor when initializing the temporary during the call to foo(), which has been deleted leading to a compilation error. Clang handles this just fine, even with -fno-elide-constructors.
Can anyone explain why GCC is allowed to call the move constructor in this case? Isn't result an lvalue?
I'm gonna quote C++17 (n4659), since the wording there is the most clear, but previous revisions say the same, just less clearly to me. Here is [class.copy.elision]/3, emphasis mine:
In the following copy-initialization contexts, a move operation might
be used instead of a copy operation:
If the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic
storage duration declared in the body or parameter-declaration-clause
of the innermost enclosing function or lambda-expression, or
[...]
overload resolution to select 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.
[ 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.  — end
note ]
So this is why in fact both Clang and GCC will try to call the move c'tor first. The difference in behavior is because Clang adheres to the text in bold differently. Overload resolution happened, found a move c'tor, but calling it is ill-formed. So Clang performs it again and finds the copy c'tor.
GCC just stops in its tracks because a deleted function was picked in overload resolution.
I do believe Clang is correct here, in spirit if anything else. Trying to move the returned value and copying as a fallback is the intended behavior of this optimization. I feel GCC should not stop because it found a deleted move c'tor. Neither compiler would if it was a defaulted move c'tor that's defined deleted. The standard is aware of a potential problem in that case ([over.match.funcs]/8):
A defaulted move special function ([class.copy]) that is defined as
deleted is excluded from the set of candidate functions in all
contexts.
From [class.copy.elision]:
In the following copy-initialization contexts, a move operation might be used instead of a copy operation: If the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body [...]
overload resolution to select 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.
In return result; we are precisely in the case mentioned in that paragraph - result is an id-expression naming an object with automatic storage duration declared in the body. So, we first perform overload resolution as if it were an rvalue.
Overload resolution will find two candidates: X(X const&) and X(X&&). The latter is preferred.
Now, what does it mean for overload resolution to fail? From [over.match]/3:
If a best viable function exists and is unique, overload resolution succeeds and produces it as the result. Otherwise overload resolution fails and the invocation is ill-formed.
X(X&&) is a unique, best viable function, so overload resolution succeeds. This copy elision context has one extra criteria for us, but we satisfy it as well because this candidate has as the type of its first parameter as (possibly cv-qualified) rvalue reference to X. Both boxes are checked, so we stop there. We do not go on to perform overload resolution again as an lvalue, we have already selected our candidate: the move constructor.
Once we selected it, the program fails because we're trying to call a deleted function. But only at that point, and not any earlier. Candidates are not excluded from overload sets for being deleted, otherwise deleting overloads wouldn't be nearly as useful.
This is clang bug 31025.
result is not an lvalue after returning from foo(), but a prvalue, so it is allowed to call the move constructor.
From CppReference:
The following expressions are prvalue expressions:
a function call or an overloaded operator expression, whose return type is non-reference, such as str.substr(1, 2), str1 + str2, or it++
It is possible that Clang detected that the return value is unused and threw it away directly, while GCC didn't.

Will returning a vector<vector <int> > from a function invoke any move constructors in C++11

In C++11 will returning a vector<vector<int> > from a function invoke any move constructors? Or would the below code just make another copy of the all the vectors and their elements?
vector< vector<int> > Func() {
vector< vector<int> > vec;
//vec is filled here
return vec;
}
For stl containers of simple types is there a general to find when a move constructor is used or copy is made when returning them as values from functions?
return statements are specifically covered by the standard to be automatically treated as a move. So yes, this will invoke the move constructor.
The letter of the law for this is C++11, [class.copy]§31+32:
31 When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object ... This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cvunqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value
...
32 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.
...
(Emphasis mine)
Combined and applied to your case, this means that when returning a local variable from a function, a move is attempted first and only if that's not possible, a copy will be performed.
(And, as #BjornPollex points out, it's quite likely even the move will be elided)

Returning std::move of a local variable [duplicate]

This question already has answers here:
Using std::move() when returning a value from a function to avoid to copy
(3 answers)
Closed 9 years ago.
Let there be a class A with a move constructor. Consider this:
A get()
{
A a;
return std::move( a );
}
// later in the code
A aa = get();
Here the explicit call to std:move forces the move constructor of A to be called thus it might inhibit the return value optimization in while calling get(). Thus it is said the a better implementation of get() would be this:
A get()
{
A a;
return a;
}
But the return value optimization is not a part of C++11 standard, so WHAT IF the compiler, by some reason, decides not to perform return value optimization while calling get(). In this case a copy constructor of A will be called while returning in get() right?
So isn't the first implementation of get() more pereferible??
A compiler should use a move constructor, but I didn't see an obligation in the standard :
It's always said "Copy/move constructor" in the section concerning temporary objects
standard ISO/IEC 14882:2011 C++ :
"
12.1/9
A copy constructor (12.8) is used to copy objects of class type. A move constructor (12.8) is used to move
the contents of objects of class type.
12.8/32
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. — end note ]
"
lvalue = T &
rvalue = T &&
So, It says that first, the compiler will look if it find a move constructor, then, it will look for a move constructor.
Thus, if your compiler is conform to the standard, it will call the move constructor.
I append just that which is interesting:
"
12.8/31
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class
object, even if the copy/move constructor and/or destructor for the object have side effects.
"
...So even if there is side effects in these constructors/destructors, they can be skipped