Currently, in C++ compilers one of the rules for tail-call optimization is that the return type must be trivially destructible. (Based on analyzing GCC, Clang trunk behavior. MSVC has troubles with any non-trivial types).
Is this requirement still necessary? With C++17 return-value-optimization being mandatory, it seems like the function could still use trail-call optimization, even when the return type is non-trivial. What is the problem here, that prevents compilers from that?
@edit, code example:
#include <string>
bool h();
std::string g() {
std::string s1 = "a", s2 = "b";
if (h()) return s1;
else return s2;
}
std::string f() {
return g(); // <= here I'd expect call-tail optimization due to RVO, since it is prvalue
}
https://godbolt.org/z/YYfMr6xdd
If I understand the assembly correctly, it should be possible to replace f() function with jump.
Return value optimization is only mandatory if you are returning a temporary (more precisely: a
prvalue
- see also here on cppreference.com), e.g. in this case:If you are returning a local object, this is Named Return Value Optimization (a.k.a. "Copy Elision"), which is not mandatory (but still done most of the time), e.g. in this case:
The compiler is in this case allowed to first construct
myVec
insidefoo()
s frame, return by copy, and destroy. Obviously most compilers won't do this. Still, a destruction can happen whenfoo()
ends.I guess that this is the reason for requiring trivial destructors for tail-call optimization. If
foo()
is tail-call optimized, the standard must account for the create/return-by-copy/destory lifecycle, and thus have a way of (trivially) destroying the returned object.