3
votes

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.

4
"without converting struct to class (...)" What you have is already a class. - R. Martinho Fernandes
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. - Poodlehat
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 - Praetorian
@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
"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. - Steve Jessop

4 Answers

3
votes

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...

2
votes

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.

1
votes

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

0
votes

I solved my problem by changing the type of v1 from int to const int *, by doing this I can change the address of v1 and consequently change the value that v1 points to, but preventing any kind of editing.

So, here's my new simple struct

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

When I use A with type T, I let v2 to be edited, and when I use A with type T const I prevent any attempt to edit v2's pointed value.