如何在构造函数初始值设定项列表中创建静态对象

问题描述

对于冗长的描述,我很抱歉,我试图尽可能减少示例以说明我的问题。我有以下代码

#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;
}

如您所见,SomeType 的所有实例的 Property 成员都具有相同的参数,这意味着多个实例:

const char* const _name;
const T _minValue;
const T _maxValue;

只是浪费内存,它们可以共享。解决此问题的一种选择是使用 PropertyMetadata 类来存储此信息,例如:

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);
PropertyMetadata<double> p2Metadata("p2Name",1.0);

struct SomeType
{
    SomeType()
        : p1(p1Metadata,p2(p2Metadata,0.0)
    {
    }

    Property<int> p1;
    Property<double> p2;
};

第二种方法有效并且不浪费空间,但与第一种方法相比非常不方便。

是否可以直接在构造函数初始值设定项列表中生成具有静态存储的对象,或者以任何其他方式在 SomeType 的所有实例之间共享此元数据而无需声明静态?

注意:在这个简单的例子中,PropertyMetadata 只有很少的参数,看起来可能不会造成巨大的内存浪费,但实际上它有更多的成员,其中一些使用动态分配。

解决方法

我真的不确定这是有效的还是未定义的行为,但我把它贴在这里作为答案,因为它太渴望发表评论了。

您可以在立即调用的 lambda 中有一个 static 变量,它返回对该静态局部变量的引用。

struct SomeType
{
    SomeType()
        : p1([]() -> auto& {
            static auto pm = PropertyMetadata<int>{"p1Name",-100,100};
            return pm; 
        }(),0),p2([]() -> auto& {
            static auto pm = PropertyMetadata<double>{"p2Name",0.0,1.0};
            return pm; 
        }(),0.0)
    {
    }

    Property<int> p1;
    Property<double> p2;
};

完整代码如下:

#include <concepts>
#include <type_traits>
#include <iostream>

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>
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;
    }

public:
    const PropertyMetadata<T>& _metadata;
    T _value;
};

struct SomeType
{
    SomeType()
        : p1([]() -> auto& {
            static auto pm = PropertyMetadata<int>{"p1Name",0.0)
    {
    }

    Property<int> p1;
    Property<double> p2;
};

int main()
{
    SomeType test1;
    SomeType test2;

    return 0;
}
,

可以在块范围内创建静态变量。 C++ 标准涵盖了它们只被初始化一次。

在此处查看更多信息:https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables
在这里:https://www.modernescpp.com/index.php/thread-safe-initialization-of-data#h3-static-variables-with-block-scope

最后一个链接显示了单例模式的概念,其中静态变量的使用方式如下:

class MySingleton{
public:
  static MySingleton& getInstance(){
    static MySingleton instance;
    // volatile int dummy{};
    return instance;
  }
private:
  MySingleton()= default;
  ~MySingleton()= default;
  MySingleton(const MySingleton&)= delete;
  MySingleton& operator=(const MySingleton&)= delete;

};

所以你可以用类似的方式初始化一个静态变量。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...