struct with const member

22,282

Solution 1

Here's a way to "expose" a public, read-only data member that is modifiable by the class's own member functions (including assignment):

template <typename T>
class Helper {
    friend class A;
    T *ptr;
    Helper &operator=(const Helper &rhs) = default; // in C++11
    Helper &operator=(const Helper &rhs) { ptr = rhs.ptr; } // in C++03
  public:
    Helper(T *ptr) : ptr(ptr) {}
    operator const int &() const { return *ptr; }
};

class A {
    int v1_;
  public:
    Helper<int> v1;
    A() : v1(&v1_) {} // although `A` should have a constructor that sets `v1_`
    A(const A &rhs) { v1_ = rhs.v1_; v1 = Helper<int>(&v1_); }
    A &operator=(const A &rhs) { v1_ = rhs.v1_; v1 = Helper<int>(&v1_); }
};

Now anyone outside the class A can use v1, but the only thing they can use it for is to get a const int& reference to v1_.

It is far easier just to give A a getter function that returns const int &, but if you really want the data member syntax then this provides it...

Solution 2

It says that you cannot use the default assignment operator. Nothing keeps you from writing your own operator= and use a const_cast. Unfortunately, this will be undefined behavior as v1 is declared const. So I'd suggest that you use accessors and private data.

Solution 3

You could just make it a class with everything public (thats all a struct is) and use an initialisation list - no need for getters/setters

Share:
22,282
Vektor88
Author by

Vektor88

Updated on February 11, 2020

Comments

  • Vektor88
    Vektor88 about 4 years

    I'm having some trouble with a template struct.

    template<typename T>
    struct A{
        const int v1;
        T* v2;
    };
    

    My purpose is to make v1 always not editable, while v2 should be editable if I use T and not editable if I use const T as type.

    If I create constructor to initialize A, the struct becomes:

    template<typename T>
    struct A{
        const int v1;
        T* v2;
        A() : v1(-1) { v2=NULL; }
        A(int value) : v1(value) { v2=NULL; }
    };
    

    But then g++ says that I need a specific assignment operator:

    error: non-static const member ‘const int A::v1’, can’t use default assignment operator

    But my assignment operator should also allow editing of v1. The only thing I'd like to avoid is an edit from the outside, something like:

    A a;
    a.v1=10;
    

    Is there any way to implement this (without creating getter/setter or using a pointer to a new A(int) with the desired value)?

    What if I declare v1 as a const int * ? It could refer someway to some value, but it cannot edit it.

    • R. Martinho Fernandes
      R. Martinho Fernandes about 12 years
      "without converting struct to class (...)" What you have is already a class.
    • Poodlehat
      Poodlehat about 12 years
      You have to understand that in C++ a struct IS a class, just a very specific type of class. A struct is a class where everything is public.
    • Praetorian
      Praetorian about 12 years
      If the assignment operator is able to modify const members then you still have the problem of edit from the outside because I can just perform an assignment of the whole object instead of modifying obj.v1 by itself
    • Poodlehat
      Poodlehat about 12 years
      Why exactly do you not want to add getter/setters, by the way?
    • Praetorian
      Praetorian about 12 years
      const mutable would've come in very handy :-)
    • Vektor88
      Vektor88 about 12 years
      @Poodlehat because A has to be a struct, so I cannot use private data.
    • Vektor88
      Vektor88 about 12 years
      What if I remove the constructors and declare v1 as const int*, then I could change the address v1 points to, but not the value of the pointed value.
    • Steve Jessop
      Steve Jessop about 12 years
      @Poodlehat: "A struct is a class where everything is public". No, a struct is a class where members and bases are public by default. Structs can still have private members. It basically doesn't matter which you use of struct and class, as long as you're consistent all it affects is where you need to type access specifiers. It can also affect how the name is mangled.
    • Steve Jessop
      Steve Jessop about 12 years
      "My purpose is to make v1 always not editable" ... "my assignment operator should also allow editing of v1" -- there's your problem, directly contradicting requirements. Pick one of them to give up.
    • Vektor88
      Vektor88 about 12 years
      @SteveJessop I'm thinking to declare v1 as const int* , so that I can change v1 address (and the value v1 points to), but I can't edit it.
    • Steve Jessop
      Steve Jessop about 12 years
      @Vektor88: but then whoever it is you were trying to prevent from changing the value of v1 when it was a const int, can change the pointer value now that it's a const int *. Plus you have the question of how the ints are allocated and freed. There is no good way to allow the copy assignment operator to change the value, while preventing other member functions from changing it. Neither is there a good way to expose a non-const member to the public in a way that prevents them from writing it. C++ doesn't have that fine-grained encapsulation built in, you have to roll your own.
    • Vektor88
      Vektor88 about 12 years
  • Paul Manta
    Paul Manta about 12 years
    Is it still undefined behavior if you use const_cast?
  • Martin Beckett
    Martin Beckett about 12 years
    @pmr - Correct, but if it's a const I would really prefer to set it at ctor time anyway. Your solution is better for his question
  • pmr
    pmr about 12 years
    @PaulManta The standard is quite clear here: const_cast results in undefined behaviour iff the object has been declared const. This is the case for the member variable. I can dig up the quotes, if it makes you feel better.
  • Paul Manta
    Paul Manta about 12 years
    Oh, I see. Good think you put emphasis on "declared", I wouldn't have noticed it.
  • Vektor88
    Vektor88 about 12 years
    Looks better than my solution. Thank you!