I had a problem that appeared in GCC and Clang, but not MSVC. The problematic part boils down to this:
#include <utility>
#include <string>
#include <iostream>
auto mkStrings() -> std::pair<std::string, std::string>
{
std::string r = "r";
return { r, "s" + std::move(r) }; // !
}
int main()
{
auto [r, s] = mkStrings();
std::cout << "'" << r << " " << s << "'" << std::endl;
}
On MSVC, I get:
'r sr'
On GCC 12.2.0 and Clang 15.0.7, it outputs:
' sr'
(On Clang 16.0.1 the compiler segfaults.)
I’m quite convinced the std::move is a problem, but I don’t understand why. I even looked up if I was mistaken that initializer lists are evaluated left-to-right, but according to the answers to this question they are.
Of course, I just removed the std::move, but I’d like to understand why I had to.
The following constructor is used for the initialization:
(In C++23, default template arguments will be added, but this doesn't affect the answer.)
Because you're using braced initialization, the parameter
xwill be initialized before the parametery. However bothxandymust be initialized before the ctor-initializer can be evaluated (which actually initializes thefirstandsecondmembers).So
xis first bound tor, thenyto the temporary result of the expression"s" + std::move(r). At this point,rmay be moved from.Afterward, the constructor uses
std::forward<U1>(x)to initializefirst. But at this point, the move from the object thatxrefers to has already been performed.