0

I'm using a library that defines some data type classes usually implemented as tight wrappers around a std::vector<>. The type hierarchy is several layers deep, mostly only adding elaborated constructors.

My problem: the base class defines its std::vector as private (which is fine), but only adds an accessor method as const. The derived class doesn't even have access to it. The library looks like this (shorted for clarity):

template <class T> class BaseList
{
public:
    BaseList (const T data0) : data_ (1) { 
       data_[0] = data0; }

    const T & operator[] (const size_t nr) const { 
       // does out off bounds check here       
       return data_[nr]; }

private:
    std::vector<T> data_;
}

class FancyClass : public BaseList<SomeEnumType>
{
public:
    FancyClass (const SomeOtherEnumType data0) 
        : BaseList<SomeEnumType> ( static_cast<SomeEnumType> (data)) 
        {}
}

Now as I see it, the const definition is completely bogus. No internal method relies on the vector really being constant, neither do I in my external code.

What I like to do is simply this:

strukt MyType {
    FancyClass myData;
    bool otherData;
}

int main() {
    MyType storage = {FancyClass(0), false};

    storage.myData[0] = 5;
}

Which of course doesn't work because of the const-ness. ("assignment of read-only location")

With the responsibility completely on my side: is there some const_cast magic I could do to make this structure writable? The only other possibility I know would be to completely replicate the type hierarchy in my code, but this would still leave me with either a lot of casts or a toFancyClass() function to call whenever I interface library code.

Any Ideas? Thanks!

(Please don't comment on the code quality of the library. If I could change that, I wouldn't be asking this question ...)

Chaos_99
  • 2,284
  • 2
  • 25
  • 29

3 Answers3

1

The simplest would be to add non-const version of the operator[]. Otherwise, you might cause an UB by casting away the constness with using const_cast.

You can throw away the constness like this :

int main() {
    MyType storage = {FancyClass(0), false};

    const_cast< SomeEnumType& >( storage.myData[0] ) = 5;
}
BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • Says "Invalid const_cast from Type '`const SomeEnumType`' to type '`FancyClass&`' in my code. – Chaos_99 Dec 03 '12 at 13:44
  • 1
    @Chaos_99: 1. You can't change the type via a const_cast. 2. BaseList => T == SomeEnumTypee, so you'd have to cast to `SomeEnumType &`. – MFH Dec 03 '12 at 13:48
  • @Chaos_99 Right,fixed. I didn´t check what type is returned in the operator[] – BЈовић Dec 03 '12 at 13:57
  • Yes, now it works. I'm still not able to type that away without getting strangled by all the references, constness and types that you need to get right for that code to work. Maybe in a few years ... – Chaos_99 Dec 03 '12 at 14:22
  • It's now a duplicate of user93353's answer (or the other way around). I'll give the points to the first one to come up with a more detailed definition of when this produces UB and when not. (Especially in references to the posted sample code.) But I'm already satisfied and thankful. – Chaos_99 Dec 03 '12 at 14:25
1

Now as I see it, the const definition is completely bogus. No internal method relies on the vector really being constant, neither do I in my external code.

The const qualifier on a method doesn't mean it relies on the vector being constant. It means that the method will not modify the state of the object. It is added so that the following code will compile.

void f(const FancyClass a)
{
    cout<<a[0];
}

The above code will not above compile without the const qualifiers on the [] method.

Anyway, the following should work

SomeEnumType & r = const_cast<SomeEnumType &>(storage.myData[0]);

r = b;

where b is an enum of type SomeEnumType

However if your storage object is actually a const object, then it will lead to undefined behaviour.

user93353
  • 13,733
  • 8
  • 60
  • 122
  • Thanks! This actually compiles! But what exactly do you mean by 'is actually a const object'? The underlying vector is not, neither is my struct member or my `storage` variable. The only other occurrence of const is within the constructors parameter list, but I think this doesn't make the object itself const, right? – Chaos_99 Dec 03 '12 at 14:07
  • @Chaos_99 - Here is an example of 'is actually a const object' - http://stackoverflow.com/a/357607/922712 But the example is for a `char *`. It should be possible to extrapolate the example to a const object of a class. For eg. the storage variable could be a const object in which case, this would lead to undefined behaviour. – user93353 Dec 03 '12 at 15:03
0

Normally operator[] comes in pairs of a mutable and a const version.. (See: Operator overloading)

Of course there is a const_cast-based solution, but it involves undefined behaviour (you're not allowed to change a value after casting away constness!)

Community
  • 1
  • 1
MFH
  • 1,664
  • 3
  • 18
  • 38
  • 1
    You are allowed to change any value *that is not actually const*. Casting away the constness or not does not have any bearing in that. – R. Martinho Fernandes Dec 03 '12 at 13:36
  • Based on §5.2.11.7 of N3485 I'm tempted to disagree `[ Note: Depending on the type of the object, a write operation through the pointer, lvalue or pointer to data member resulting from a const_cast that casts away a const-qualifier73 may produce undefined behavior (7.1.6.1). —end note ]` – MFH Dec 03 '12 at 13:46
  • Yes, adding that second, mutable operator[] version would be the best solution. Unfortunately, I'm not allowed to modify even the header files of that library. And I know of no way to add it within one of my own source files, especially because it needs to be defined for the base class, not the `FancyClass` I'm using. – Chaos_99 Dec 03 '12 at 13:48
  • 1
    @MFH That agrees with what R. says. ""may" produce undefined behavior" because it's only undefined behaviour if the object isn't *defined* as `const`. Something like `int i = 3; const_cast(const_cast(i))++;` is perfectly valid. –  Dec 03 '12 at 13:48
  • @Chaos_99: If you can't modify the headers your only bet is BЈовић's solution (look at my comment on why it failed to compile) – MFH Dec 03 '12 at 13:58