4

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.

12
  • "without converting struct to class (...)" What you have is already a class. Commented Feb 9, 2012 at 20:49
  • 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. Commented Feb 9, 2012 at 20:51
  • 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 Commented Feb 9, 2012 at 20:53
  • 1
    @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. Commented Feb 9, 2012 at 21:15
  • 2
    "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. Commented Feb 9, 2012 at 21:19

4 Answers 4

3

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

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

1 Comment

Looks better than my solution. Thank you!
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.

3 Comments

Is it still undefined behavior if you use const_cast?
@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.
Oh, I see. Good think you put emphasis on "declared", I wouldn't have noticed it.
1

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

1 Comment

@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
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.