I was converting these tables of docstrings (as a type traits like system) for some domain object classes until I stumpled upon this issue. Later, I was planning to check at compile time if these special members had documentation written (as a to reason to why I would like it at compile time). I created a small example for demonstration:
#include <initializer_list>
struct CStr {
struct M {
const char* name;
const char* val;
};
constexpr CStr(const std::initializer_list<M>& str) : str_(str) {
};
std::initializer_list<M> str_;
};
constexpr CStr cstr_test{ { {"key", "val"} } };
int main() {
return cstr_test.str_.begin()->name[0];
}
Basicly the 3 main compilers seems to treat this case differently, this seemingly works on an older gcc, but fails to compile when changed to latest version 9.1 (saying the cstr_test line is not a contant expression). On msvc (and the one im interested in) the initializer_list
gets the size correct but the temporary does not survive to the output. clang refused to compile, due to a temporary being created. This last thing seems like a hint, and Im suspecting this is what could be happening with msvc as well but without emitting the error.
The example can be fixed if std::initializer_list<M>
is changed to just M
.
A relevant section from the reference might be:
The underlying array is a temporary array of type const T[N], in which each element is copy-initialized (except that narrowing conversions are invalid) from the corresponding element of the original initializer list. The lifetime of the underlying array is the same as any other temporary object, except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary (with the same exceptions, such as for initializing a non-static class member). The underlying array may be allocated in read-only memory.
How can a list of objects containing strings be created at compile time ?
std::initializer_list
should not be used as storage.
So in your case storage should be done outside of the class. Regular C-array or std::array
can be used for that.
Then you class may just have view of that data.
C++20 provides std::span
.
Prior to C++20, you might do something like:
struct CStr {
struct M {
const char* name;
const char* val;
};
constexpr CStr(const M* data, std::size_t size) : data(data), size(size) {}
const M* begin() const { return data; }
const M* end() const { return data + size; }
const M* data;
std::size_t size;
};
constexpr CStr::M data[]{ { {"key", "val"} } };
constexpr CStr cstr_test(data, 1);
int main() {
return cstr_test.begin()->name[0];
}