21

I know of const, that can't be changed after creation. But I was wondering if there is a way to declare a variable that you set only once and after that, can't overwrite. In my code, I would like to avoid the bool variable by having an nFirst that, once set to nIdx, can't be set to the new value of nIdx.

My code:

 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++; } 
5
  • 5
    None built-in AFAIK, but you can roll your own class that does this quite easily. Commented Mar 4, 2016 at 16:09
  • No, it's not possible with standard features. Commented Mar 4, 2016 at 16:09
  • 2
    You can create your own class for this. But the original boolean approach is what that class will have to use too, so there is no difference in performance. Commented Mar 4, 2016 at 16:09
  • 1
    std::call_once, static variables or simply checking if it's already set to nIdx. Yeah, it does not stop you from assigning it to something else by accidents somewhere else in your code, but your case doesn't seem like it need that anyway. Commented Mar 4, 2016 at 16:10
  • I also wish that C/C++ had a feature for making a variable const so that any code following (in program order in the current block) would not be able to modify it. Example: Class1 x; if (expr) { Class2 obj(); x = obj.f(); } else { x.doStuff(); } const x; /* Code following sees x as const Class1 */. Commented Mar 4, 2016 at 18:58

5 Answers 5

19

Pretty easy to roll your own.

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

Then just use the wrapper class for your variable.

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

Outputs:

1

Sign up to request clarification or add additional context in comments.

1 Comment

@Jarod42 Fixed, used a bool first to track it.
11

I would like to avoid the bool variable

You can check nFirst itself, based on the fact that it won't be set a negative number. Such as:

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

5 Comments

@eugenesh any variable with a const prefix cannot be altered, eg. const double pi=3.141592653;
This seems like the most straight forward approach and definetly avoids the boolean. Thank you!
also you can enum enum bool{false,true} enum color{red=1,blue=16,green=256} and you can have const enum or typedef enum.
@tzippy accept the answer if it helped you solve your issue.
If the number can be negative, you can choose some other value to be your uninitialized value, e.g. std::numeric_limits<int>::max() or something. (If that's ever a legitimate value for your number, you probably shouldn't be using an int in the first place.)
2

Similar to cocarin's, but throws exception instead of silently ignoring assignment:

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

Usage:

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

Online demo

Comments

1

Your question is about avoiding the bool but also implies the need for const-ness.

To avoid the bool, I'd use a boost::optional like this:

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

Then, you can enforce logical (rather than literal) const-ness like this:

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 

Using const_cast is not the safest practice, but in your particular case and as long as you only do it once it'd be OK. It simplifies both your code and your intentions: you do want a const, it's just that you want to defer it's initialisation for a bit.

Now, as songyuanyao suggested, you could directly use an int instead of a boost::optional, but the latter makes your intention explicit so I think it's better this way. In the end of day this is C++ while songyuanyao's solution is really a C-style one.

2 Comments

An optional doesn't really avoid a bool, it hides it inside another class.
Isn't using const_cast to modify constant object undefined behavior? stackoverflow.com/q/14154382
1

This is set once template. You can use this class as assurance that the value will be set and saved only once. Every next try will be canceled.

#include <iostream> using namespace std; template <class T> class SetOnce; template<class T> std::ostream& operator<<( ostream& os, const SetOnce<T>& Obj ); template <class T> class SetOnce { public: SetOnce() {set = false; } ~SetOnce() {} void SetValue(T newValue) { value = !set ? newValue : value; set = true; } private: T value; bool set; friend std::ostream& operator<< <>( ostream& os, const SetOnce& Obj ); public: SetOnce<T>& operator=( const T& newValue ) { this->SetValue(newValue); return *this; } }; template<class T> std::ostream& operator<<( ostream& os, const SetOnce<T>& Obj ) { os << Obj.value; return os; } 

Use case:

int main() { SetOnce<bool> bvar; SetOnce<int> ivar; SetOnce<std::string> strvar; std::cout<<"initial values: \n"<<bvar<<" " <<ivar<<" "<<strvar<<" \n\n"; bvar = false; //bvar.SetValue(false); ivar = 45; //ivar.SetValue(45); strvar = "Darth Vader"; //strvar.SetValue("Darth Vader"); std::cout<<"set values: \n"<<bvar<<" " <<ivar<<" "<<strvar<<" \n\n"; bvar = true; //bvar.SetValue(true); ivar = 0; //ivar.SetValue(0); strvar = "Anakin"; //strvar.SetValue("Anakin"); std::cout<<"set again values: \n"<<bvar<<" " <<ivar<<" "<<strvar<<" \n\n"; return 0; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.