如何在构造函数初始化列表中创建静态对象

3

很抱歉描述比较长,我尽可能地缩减了例子以说明我的问题。我有以下代码:

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

第二种方法可行且不会浪费空间,但与第一种方法相比非常不方便。
是否可以在构造函数初始化列表或其他方式中直接生成具有静态存储的对象,或者有没有其他方法可以在不声明静态变量的情况下在所有 SomeType 实例之间共享这些元数据?
注意:在这个简单的示例中, PropertyMetadata 只有几个参数,可能看起来并不会浪费太多内存,但实际上它有更多成员,并且其中一些成员使用动态分配。
2个回答

1
我其实不确定这是否是有效的或未定义的行为,但我在这里发布它作为一个答案,因为它太长了,不适合作为评论。
你可以在立即调用的 lambda 中有一个静态变量,该 lambda 返回对该静态局部变量的引用。
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", -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;
};

int main()
{
    SomeType test1;
    SomeType test2;

    return 0;
}

我认为这是正确的,我根据 https://stackoverflow.com/users/12024331/florian-thonig 的答案尝试了类似的东西。看起来使用起来相当复杂,但我认为可以通过一些宏来改善。 - Mircea Ispas

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;

};

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接