Disclaimer:
My first impression was that this code abuses class and struct for what namespace was actually invented for.
This said, the answer focusses on the technical issues with the static data members in OPs code.
To reproduce the issue, I made the following similar example:
#include <iostream>
#include <string>
class Class1
{
public:
Class1() = delete ;
const static int x = 1;
static struct ZZ {
static const int z1 = 1;
static const std::string z2 = "word";
} z;
};
int main(int argc, char* argv[])
{
std::cout << Class1::z.z2;
}
Output:
main.cpp:11:34: error: in-class initialization of static data member 'const string Class1::ZZ::z2' of non-literal type
11 | static const std::string z2 = "word";
| ^~
main.cpp:11:39: error: temporary of non-literal type 'const string' {aka 'const std::__cxx11::basic_string<char>'} in a constant expression
11 | static const std::string z2 = "word";
| ^~~~~~
Live Demo on coliru
IMHO, this is close enough to the issue of OP.
TL;DR: The problem is that static member variables must be either explicitly defined (non-inlined) or remarked as inline.
So, my fix would look like this:
#include <iostream>
#include <string>
class Class1
{
public:
Class1() = delete ;
const static int x = 1;
inline static struct ZZ {
static const int z1 = 1;
inline static const std::string z2 = "word";
} z = ZZ{};
};
int main(int argc, char* argv[])
{
std::cout << Class1::x << '\n'
<< Class1::z.z1 << '\n'
<< Class1::z.z2 << '\n';
}
Output:
1
1
word
Live Demo on coliru
Note:
While the inline is necessary for struct ZZ and std::string, it can be left out for integral types. Hence, static const int z1 = 1; will be accepted without complaints (without prefixing it with inline).
Further reading:
Static data members on cppreference.com
For the comparison, the same code made working without inline:
#include <iostream>
#include <string>
class Class1
{
public:
Class1() = delete ;
const static int x = 1;
static struct ZZ {
static const int z1 = 1;
static const std::string z2;
} z;
};
//Class1::ZZ Class1::z;
const std::string Class1::ZZ::z2 = "word";
int main(int argc, char* argv[])
{
std::cout << Class1::x << '\n'
<< Class1::z.z1 << '\n'
<< Class1::z.z2 << '\n';
}
Output:
1
1
word
Live Demo on coliru
Note:
About const Class1::ZZ Class1::z;, I was not quite sure. The missing definition might be left out as Class1::z is never used itself.
To check this out, I modified the sample again:
#include <iostream>
#include <string>
class Class1
{
public:
Class1() = delete ;
const static int x = 1;
static struct ZZ {
static const int z1 = 1;
static const std::string z2;
} z;
};
Class1::ZZ Class1::z;
const std::string Class1::ZZ::z2 = "word";
std::ostream& operator << (std::ostream &out, const Class1::ZZ &z)
{
return out << z.z1 << ", " << z.z2;
}
int main(int argc, char* argv[])
{
std::cout << Class1::x << '\n'
<< Class1::z << '\n'
<< Class1::z.z1 << '\n'
<< Class1::z.z2 << '\n';
}
Output:
1
1, word
1
word
Live Demo on coliru
Note:
Commenting the definition of Class1::z (like in the previous sample) results in a link error.