I'm sorry for the long description, I tried to reduce the example as much as possible to illustrate my problem. I have the following code:
#include <concepts> #include <type_traits> template<typename T> concept NumericPropertyValue = (std::is_integral_v<T> && !std::is_same_v<T, bool>) || std::is_floating_point_v<T>; template<NumericPropertyValue T> class Property { public: Property(const char* name, T minValue, T maxValue, T defaultValue) : _name(name) , _minValue(minValue) , _maxValue(maxValue) , _value(defaultValue) { } T operator=(T value) { // check the value, ... return _value; } operator T() const { return _value; } // some other utility functions private: const char* const _name; const T _minValue; const T _maxValue; T _value; }; struct SomeType { SomeType() : p1("p1Name", -100, 100, 0) , p2("p2Name", 0.0, 1.0, 0.0) { } Property<int> p1; Property<double> p2; }; int main() { SomeType test1; SomeType test2; return 0; } As you can see all instances of SomeType have their Property members with the same parameters, meaning that more than one instance of:
const char* const _name; const T _minValue; const T _maxValue; is just wasting memory and they could be shared. One option to fix this problem is to have a PropertyMetadata class to store this info, something like:
template<NumericPropertyValue T> struct PropertyMetadata { const char* const _name; const T _minValue; const T _maxValue; }; template<NumericPropertyValue T> class Property { public: Property(const PropertyMetadata<T>& metadata, T defaultValue) : _metadata(metadata) , _value(defaultValue) { } T operator=(T value) { // check the value, ... return _value; } operator T() const { return _value; } private: const PropertyMetadata<T>& _metadata; T _value; }; PropertyMetadata<int> p1Metadata("p1Name", -100, 100); PropertyMetadata<double> p2Metadata("p2Name", 0.0, 1.0); struct SomeType { SomeType() : p1(p1Metadata, 0) , p2(p2Metadata, 0.0) { } Property<int> p1; Property<double> p2; }; The second approach works and it doesn't waste space, but it's very inconvenient compared to the first approach.
Is it possible to generate objects with static storage directly in the constructor initializer list or any other way to share this metadata between all instances of SomeType without having to declare statics?
Note: In this simple example PropertyMetadata has only few parameters and it might not seem like a huge waste of memory, but in reality it has more members and some of them use dynamic allocation.