I'm trying to create a C++ program with MSVC 12 (Visual Studio 2013, Update 4) which uses a std::initializer_list
of structs which have std::string
members. I seem to have run into a bug in MSVC. Here's a minimal example which exhibits the problem:
#include <cassert>
#include <initializer_list>
#include <string>
namespace
{
struct TestStructure
{
std::string m_string;
int m_integer;
TestStructure(const std::string& string, int integer)
: m_string(string), m_integer(integer)
{
}
TestStructure(const TestStructure&) = default;
~TestStructure() = default;
TestStructure& operator=(const TestStructure&) = default;
};
}
int main(int, char **)
{
TestStructure structure("foobar", 12345);
std::initializer_list<TestStructure> structures({structure});
assert(structure.m_integer == 12345);
assert(structure.m_string == "foobar");
assert(structures.size() == 1);
assert(structures.begin()->m_integer == 12345);
assert(structures.begin()->m_string == "foobar"); // abort()'s here.
return EXIT_SUCCESS;
}
I would expect that this program would compile and execute without any problems. However, when I run it the last assertion seems to fail. Looking in the Visual Studio debugger, it would seem that structures.begin()->m_string == ""
.
Is my program somehow not well-formed, or is this actually a bug in MSVC? Is there some workaround for this problem (other than just not using initializer lists)?
The problem is that you're using both parentheses and braces:
This will construct a temporary
std::initializer_list<TestStructure>
and copy it tostructures
; the normal lifetime-extension will not be performed, sostructures
will be pointing to destructed storage:[dcl.init.list]:
Note that clang agrees with MSVC on this; gcc performs lifetime extension on the backing array, but it is erroneous to do so (Bug filed: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66476).
If you want copy-initialization (with lifetime extension), use an equals sign:
Otherwise, use direct-list-initialization (using braces directly):