Search code examples
c++17immutability

Way to defensive check value assigned to public const variable in immutable class in C++17?


Coming back to C++ after a hiatus in Java. Attempting to create an immutable object and after working in Java, a public const variable seems the most sensible (like Java final).

public:
     const int A;

All well and good, but if I want to defensive check this value, how might I go about it. The code below seems strange to me, but unlike Java final members, I can't seem to set A in the constructor after defensive checks (compiler error).

MyObj::MyObj(int a) : A(a) {
        if (a < 0)
            throw invalid_argument("must be positive");
    }

A public const variable for A seems like a clearer, cleaner solution than a getter only with a non const int behind it, but open to that or other ideas if this is bad practice.


Solution

  • Your example as it stands should work fine:

    class MyObj {
    public:
      const int var;
      MyObj(int var) : var(var) {
        if (var < 0)
           throw std::invalid_argument("must be positive");
      }
    };
    

    (Live example, or with out-of-line constructor)

    If you intend that MyObj will always be immutable, then a const member is probably fine. If you want the variable to be immutable in general, but still have the possibility to overwrite the entire object with an assignment, then better to have a private variable with a getter:

    class MyObj {
      int var;
    public:
      MyObj(int var) : var(var) {
        if (var < 0)
          throw std::invalid_argument("must be positive");
      }
    
      int getVar() const { return var; }
    };
    
    // now allows
    MyObj a(5);
    MyObj b(10);
    a = b;
    

    Edit

    Apparently, what you want to do is something like

      MyObj(int var) {
        if (var < 0)
           throw std::invalid_argument("must be positive");
        this->var = var;
      }
    

    This is not possible; once a const variable has a value it cannot be changed. Once the body ({} bit) of the constructor starts, const variables already have a value, though in this case the value is "undefined" since you're not setting it (and the compiler is throwing an error because of it).

    Moreover, there's actually no point to this. There is no efficiency difference in setting the variable after the checks or before them, and it's not like any external observers will be able to see the difference regardless since the throw statement will unroll the stack, deconstructing the object straight away.