【问题标题】:Is it possible to define a variable that can be set only once?是否可以定义一个只能设置一次的变量?
【发布时间】:2016-06-18 11:01:27
【问题描述】:

我知道 const,它在创建后无法更改。但是我想知道是否有一种方法可以声明一个只设置一次的变量,之后就不能覆盖了。 在我的代码中,我想通过设置一个 nFirst 来避免 bool 变量,一旦设置为 nIdx,就不能将其设置为 nIdx 的新值。

我的代码:

    int nFirst = 0;
    int nIdx = 0;
    bool bFound = false;
    BOOST_FOREACH(Foo* pFoo, aArray)
    {
        if (pFoo!= NULL)
        {
            pFoo->DoSmth();
            if (!bFound)
            {
                nFirst= nIdx;
                bFound = true;
            }
        }
        nIdx++;
    }

【问题讨论】:

  • 没有内置的 AFAIK,但您可以创建自己的类来轻松完成此操作。
  • 不,使用标准功能是不可能的。
  • 您可以为此创建自己的类。但是原始的布尔方法也是该类必须使用的,因此性能没有差异。
  • std::call_once, static 变量或只是检查它是否已设置为 nIdx。是的,它不会阻止您在代码中的其他地方意外地将其分配给其他东西,但您的情况似乎并不需要它。
  • 我还希望 C/C++ 具有创建变量 const 的功能,这样任何后续代码(按当前块中的程序顺序)都无法修改它。示例:Class1 x; if (expr) { Class2 obj(); x = obj.f(); } else { x.doStuff(); } const x; /* Code following sees x as const Class1 */.

标签: c++


【解决方案1】:

类似于cocarin's,但抛出异常而不是默默地忽略赋值:

template <typename T, typename Counter = unsigned char>
class SetOnce {
public:
    SetOnce(const T& initval = T(), const Counter& initcount = 1):
        val(initval), counter(initcount) {}
    SetOnce(const SetOnce&) = default;
    SetOnce<T, Counter>& operator=(const T& newval) {
        if (counter) {
            --counter;
            val = newval;
            return *this;
        }
        else throw "Some error";
    }
    operator const T&() const { return val; } // "getter"
protected:
    T val;
    Counter counter;
};

用法:

SetOnce<int> x = 42;
std::cout << x << '\n'; // => 42
x = 4;
// x = 5; // fails
std::cout << x << '\n'; // => 4

Online demo

【讨论】:

    【解决方案2】:

    我想避免使用 bool 变量

    您可以检查nFirst 本身,因为它不会被设置为负数。如:

    int nFirst = -1;
    int nIdx = 0;
    BOOST_FOREACH(Foo* pFoo, aArray)
    {
        if (pFoo != NULL)
        {
            pFoo->DoSmth();
            if (nFirst == -1)
            {
                nFirst = nIdx;
            }
        }
        nIdx++;
    }
    

    【讨论】:

    • @eugenesh 任何带有 const 前缀的变量都不能被更改,例如。 const double pi=3.141592653;
    • 这似乎是最直接的方法,并且明确地避免了布尔值。谢谢!
    • 你也可以枚举 enum bool{false,true} enum color{red=1,blue=16,green=256} 而你可以有 const enum 或 typedef enum。
    • @tzippy 如果它可以帮助您解决问题,请接受答案。
    • 如果数字可以为负数,您可以选择其他值作为您的未初始化值,例如std::numeric_limits&lt;int&gt;::max() 什么的。 (如果这对您的号码来说是一个合法的值,那么您可能一开始就不应该使用int。)
    【解决方案3】:

    您的问题是关于避免使用 bool,但也暗示需要 const-ness。

    为了避免 bool,我会像这样使用 boost::optional:

    boost::optional<int> nFirst;
    // .. 
    if (!nFirst) nFirst = nIdx;
    // and now you can use *nFirst to get its value
    

    然后,您可以像这样强制执行逻辑(而不是文字)常量:

    const boost::optional<int> nFirst;
    // ..
    if (!nFirst) const_cast<boost::optional<int>&>(nFirst) = nIdx;
    // you can use *nFirst to get the value, any attempt to change it would cause a compile-time error
    

    使用const_cast 不是最安全的做法,但在您的特定情况下,只要您只这样做一次就可以了。它简化了您的代码和您的意图:您确实需要一个 const,只是您想将它的初始化推迟一点。

    现在,正如 songyuanyao 建议的那样,您可以直接使用 int 而不是 boost::optional,但后者使您的意图明确,所以我认为这样更好。归根结底,这是 C++,而宋元耀的解决方案实际上是 C 风格的。

    【讨论】:

    • optional 并不能真正避免 bool,它会将其隐藏在另一个类中。
    • 不是使用const_cast 来修改常量对象未定义的行为吗? stackoverflow.com/q/14154382
    【解决方案4】:

    很容易自己动手。

    template<typename T>
    class SetOnce
    {
    public:
        SetOnce(T init) : m_Val(init)
        {}
    
        SetOnce<T>& operator=(const T& other)
        {
            std::call_once(m_OnceFlag, [&]()
            {
                m_Val = other;
            });
    
            return *this;
        }
    
        const T& get() { return m_Val; }
    private:
        T m_Val;
        std::once_flag m_OnceFlag;
    };
    

    然后只需为您的变量使用包装类。

    SetOnce<int> nFirst(0);
    nFirst= 1;
    nFirst= 2;
    nFirst= 3;
    
    std::cout << nFirst.get() << std::endl;
    

    输出:

    1

    【讨论】:

    • @Jarod42 已修复,先使用 bool 进行跟踪。
    猜你喜欢
    • 2015-02-19
    • 1970-01-01
    • 2018-01-04
    • 1970-01-01
    • 2019-10-21
    • 2018-10-08
    • 1970-01-01
    • 2017-11-06
    • 1970-01-01
    相关资源
    最近更新 更多