I know std::array doesn't do move semantics because it's not dynamically allocated. Do Compilers do proper NRVO for it? What about in the context of the calling code being a constructor initializer list?
Code is below and on goldbolt here: https://godbolt.org/z/je1Mvj15P
NB The share link passes argv[1] = "12345678901234567890". Also it is using only -Og to keep the assembly readable. At -O3 it starts unrolling loops etc, but this does not affect the question I believe.
The constructor init list call arr_(get_arr(s)) has no choice but to copy, because move its not available. Unless the compiler is doing full NRVO (which is "not mandatory", see comments below).
The compiler explorer output seems to show no copying to my eyes.
Is NRVO saving this?
Is this idiomatic / good? Or is std::array the wrong choice here? Or maybe this way of initialising an non-movable aggregate member with a function call and therefore relying on RVO is not sensible/reliable?
Would it be better to leave arr_ uninitialised in the constructor init list and move the code from get_arr into the constructor body? Like this: https://godbolt.org/z/jxv69YvK3
#include <algorithm>
#include <array>
#include <numeric>
#include <string>
std::array<std::byte, 20> get_arr(const std::string& s) {
std::array<std::byte, 20> a;
// a silly proxy algorithm for the real thing
std::transform(s.begin(), s.end(), a.begin(), [](auto b) { return std::byte(unsigned(b) << 1U); });
return a;
}
struct S {
explicit S(const std::string& s) : arr_(get_arr(s)) {}
std::array<std::byte, 20> arr_;
};
int main(int /*argc*/, char* argv[]) {
// in reality we are reading about 600'000'000 strings from a file
S s(argv[1]);
return static_cast<int>(s.arr_[19]); // use it to avoid optimising away
}