My code is like the below. My idea is that we have a P which is movable but not copyable. And we have a OP which is an optional
wrapper of P. A gen_p
will generates a std::pair<int, OP>
, and its returned value will be "patten matched" by structured binding. Then I think the returned value is decomposited into i
and pp
. I want pp
being returned by move or RVO, at least not by copy.
#include <iostream>
#include <vector>
#include <optional>
#include <map>
struct P {
int i;
std::vector<int> v;
P(int ii, std::vector<int>&& vv): i(ii), v(std::move(vv)) {}
P(P&& other) {
i = other.i;
v = std::move(other.v);
}
P & operator=(P && other) {
i = other.i;
v = std::move(other.v);
return *this;
}
P(const P & other) = delete;
P & operator=(const P &) = delete;
};
typedef std::optional<P> OP;
OP move_out_p() {
auto gen_p = [&](){
P pp1 {1, std::vector<int>{2,3,4}};
std::optional<P> op = std::make_optional(std::move(pp1));
return std::make_pair(1, std::move(op));
};
auto&& [i, pp2] = gen_p();
// It compiles with std::move(pp2)
return pp2;
}
static_assert(std::is_move_constructible_v<P>);
void test_move_part() {
auto v = move_out_p();
printf("v size %lu\n", v.value().v.size());
}
int main() {
test_move_part();
}
And I get error
tm.cpp:87:12: error: call to implicitly-deleted copy constructor of 'OP' (aka 'optional<P>')
return pp2;
I think this error is strange. Here is my reasoning: It says that
In these initializer expressions, e is an lvalue if the type of the entity e is an lvalue reference (this only happens if the ref-qualifier is & or if it is && and the initializer expression is an lvalue) and an xvalue otherwise (this effectively performs a kind of perfect forwarding)".
Since my ref-qualifier is &&
, and gen_p()
is a not a lvalue(it is actually a prvalue), so in my case it is a perfect forwarding.
I don't know what I will get in this perfect forwarding case. However, if pp2
is a lvalue, then NRVO will happen. I am sure that NRVO is enabled in my compiler, since we need to specify -fno-elide-constructors
to disable that. If pp2
is a xvalue, it will compiles. Since std::move(pp)
creates a xvalue, and it compiles. If pp2
is a prvalue, I don't know, but I find it meaningless to require a copy constructor of a prvalue. So I think there is no case that prevents a RVO.
Also, if pp2 is a lvalue, it should be RVO-ed. I don't understand why an RVO is not happen in this case. It is recommended that we don't return a std::move(pp2) in our program. I don't know why we break this rule here. The compiled code looks strange to me.
So I am lost here? Could you explain this error to me?
` by value yet there is no compiler error/warning.
– Matt Jun 20 '23 at 18:35