Consider these two functions:
int foo(std::array<int, 10> const& v) {
auto const w = v;
int s{};
for (int i = 0; i < v.size(); ++i) {
s += (i % 2 == 0 ? v : w)[i];
}
return s;
}
int bar(std::array<int, 10> const& v) {
return std::accumulate(v.begin(),
v.end(),
0,
std::plus{});
}
They do exactly the same thing, if not for the fact that foo does it awkwardly, by making a const copy of argument v, and then picking elements alternatively from v and from the copy w.
If I change std::array<int, 10> to std::vector<int> throughout, though, the two assembly outputs differ; specifically, I can see the calls to new+memcpy and delete performed in foo, corresponding to w initialization at auto const w = v; and destruction at } respectively.
I assume that difference in the assembly maps to foo and bar behaving differently exception-wise, in that the allocation caused by auto const w = v; could fail in foo, whereas there's no allocation going on in bar.
However, is the compiler required to emit code for foo that is different from bar's?
Or could the compiler do as it does for the std::array case, i.e. work out that "if the allocation at auto const w = v; succeeds, then the execution of the code is the same as if w was changed to v, so I might as well make this simplification and even prevent any exception at all"?
Incidentally, I've also tried another case of "useless copy that does not require allocation",
int foo(std::optional<int> const& v, bool b) {
auto const w = v;
return (b ? v : w).value();
}
int bar(std::optional<int> const& v) {
return v.value();
}
and Clang generates the same assembly (except for using test in foo and cmp in bar to check if the optional has_value, but that's a non-functional difference).