28

In defining a function in an interface :

virtual void ModifyPreComputedCoeffs ( std::vector < IndexCoeffPair_t > & model_ ) = 0;

we want to specify that the vector model_ should not be altered in the sense push_back etc operations should not be done on the vector, but the IndexCoeffPair_t struct objects in the model_ could be changed. How should we specify that ?

virtual void ModifyPreComputedCoeffs ( const std::vector < IndexCoeffPair_t > & model_ ) = 0;

does not work I think.

SCFrench
  • 8,244
  • 2
  • 31
  • 61
Humble Debugger
  • 4,439
  • 11
  • 39
  • 56
  • 1
    Does not work, you think? Sure looks like it should work, did you try modifying it? – yan Jul 25 '11 at 17:03
  • 1
    @yan: No, it shouldn't work because `vector::operator[] const` and all other `const` element accessors return `vector::const_reference`. – Josh Jul 25 '11 at 17:14
  • 1
    @yan, a constant vector will only return constant references to the contents - you can't modify them. – Mark Ransom Jul 25 '11 at 17:15
  • 1
    @yan: I meant I tried, it did not work for me. Not completely certain if I was doing something wrong. Understand the reason now. Thanks Mark & user349433 – Humble Debugger Jul 25 '11 at 17:25

6 Answers6

19

Rather than passing the vector into the function, do what the standard library does and pass a pair of iterators instead.

virtual void ModifyPreComputedCoeffs ( std::vector < IndexCoeffPair_t >::iterator & model_begin, std::vector < IndexCoeffPair_t >::iterator & model_end )
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 3
    You are sidestepping the question. What if s/he needs to pass a vector of such vectors? – 6502 Jul 25 '11 at 17:05
  • @6502, that seems a bit out of scope of the original question, don't you think? – Mark Ransom Jul 25 '11 at 17:20
  • Not really. He asked how to specify such a vector and you suggested that he shouldn't pass a vector but two iterators. While this may work in this specific case with just a minor annoyance (two parameters instead of one) it doesn't scale and doesn't answer the original question (how to specify that vector). Unfortunately the answer in C++ is "you cannot, you must define your own vector class to do that". – 6502 Jul 25 '11 at 17:25
  • @6502: I don't get what you are saying about it not scaling. You can write relatively trivial flattening iterators that will handle depths quite easily. I also don't like const very much, but I don't think you have a strong point here. – Puppy Jul 25 '11 at 17:30
  • 3
    @6502: Most of the time people don't want to hear "That's impossible", they want to know how to make it happen. – Mark Ransom Jul 25 '11 at 17:34
  • @DeadMG: Suppose I want to pass to a function a const map from string to const vectors of non-const objects. How do you trivially flatten this? – 6502 Jul 25 '11 at 17:47
7

The C++ const-correctness concept is IMO way overrated. What you just discovered is one of the big limitations it has: it doesn't scale by composition. To be able to create a const vector of non-const objects you need to implement your own vector type. Note that for example even the standard library had to introduce new types for const_iterators.

My suggestion is to use const-correctness where you are forced to, and not everywhere you can. In theory const correctness should help programmers, but comes at a very high cost because of the syntax and is very primitive (just one bit, doesn't scale by composition, even requires code duplication).

Also in my experience this alleged big help is not really that big... most of the errors it catches are related to the const-correctness machinery itself and not to program logic.

Ever wondered why most languages (including ones designed after C++) didn't implement this idea?

6502
  • 112,025
  • 15
  • 165
  • 265
  • 12
    const correctness, regardless of opinion, exists in C++, and is an important feature that, if avoided, can cause major headaches and loss in time and money in production code. While I like the sentiment (and don't necessarily disagree), it's more of a comment to the original question than an answer. 6502 - you made a similar argument against the const_iterator answer from Mark Ransom. – Kit10 Dec 11 '13 at 22:54
  • 1
    @Copperpot: Yes, const correctness exists in C++ and you're forced to deal with it, liking it or not. It's one of the cases in which the alleged philosophy of "pay for what you want" doesn't apply because you've to pay for it even if you don't want it. In my experience with C++ I cannot remember even a single case (even just ONE) in which a const correctness error was preventing me from making a real mistake. Every time the problem was in wrong const declarations (most frequent case in my experience are what should be const methods not declared const). – 6502 Dec 11 '13 at 23:39
  • But it absolutely makes no sense, to have const vector of non-const elemets and not to be able to modify them (behaves like const vector of const elements). – Nuclear Jul 07 '15 at 07:09
  • 2
    This answer is misleading: the problem of access to non-const elements of the const vector can be solved with iterators (another answer), which proves that it's std::vector's problem, not the language's. – Steed Jun 30 '17 at 08:40
  • @Nuclear: It makes perfect sense if you choose your convention carefully. `my_vector const` could mean _"not resizeable, reassignable"_, `my_vector` would mean _"resizeable, not reassignable"_, and `my_vector const` would be _"not resizable, not reassignable"_. This is already how `unique_ptr` behaves - they just got the design wrong for `std::vector`, and now we're stuck with it. – Eric Feb 02 '19 at 05:21
2

This is likely to be in C++14 as std::dynarray.

Actually if the size is fixed at compile time you can use std::array. But it's probably more use for things like embedded programming, buffers, matrices and so on as often you don't know the desired size until runtime or you want it to be configurable.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
David C. Bishop
  • 6,437
  • 3
  • 28
  • 22
  • 2
    std::dynarray sounds interesting, but according to the current [cppreference.com page on dynarray](http://en.cppreference.com/w/cpp/container/dynarray), it won't be in C++14: "After reviewing national body comments to n3690, this library component was voted out from C++14 working paper into a separate Technical Specification. This container is not a part of the draft C++14 as of n3797." – Alan Sep 14 '16 at 15:38
1

Here's a generic version of MahlerFive's answer:

template<typename T>
class Mutable {
    mutable T m_val;
public:
    constexpr Mutable(T const& val) : m_val(val) { }
    constexpr Mutable(T&& val) : m_val(val) { }

    // note: all member functions are `const`
    constexpr Mutable const& operator=(T const& val) const {
        m_val = val;
        return *this;
    }
    constexpr Mutable const& operator=(T&& val) const {
        m_val = val;
        return *this;
    }

    constexpr operator T&() const {
        return m_val;
    }
};

You can then use std::vector<Mutable<T>> const in your code, which will mostly behave as intended.

Eric
  • 95,302
  • 53
  • 242
  • 374
1

If you are able to modify IndexCoeffPair_t, you could add some const member functions and use them to change some of its members by making the members mutable using the mutable keyword. This is kind of a hack though, since you would now be able to change the contents of any const IndexCoeffPair_t.

Example:

class IndexCoeffPair_t {
public:
    void changeX(int newVal) const {
        x = newVal;
    }

private:
    mutable int x;
};
MahlerFive
  • 5,159
  • 5
  • 30
  • 40
  • 1
    Something like this completely defeats the purpose of the `const` keyword. Why even bother if something being `const` means nothing at all... :) – UncleBens Jul 26 '11 at 18:16
  • Yes, in general you should avoid using `mutable`, which is why I said it's a hack. However, since the OP wants the vector to be const but not the contained objects, this would solve this specific problem. – MahlerFive Jul 27 '11 at 14:06
  • IMO, this is the greater evil. One thing is worrying about potential future misuse (?) of a single function (how often do you accidentally call `push_back`?), another thing is saying it's OK for all users to modify the observable state of a const object. – UncleBens Jul 27 '11 at 20:46
  • A compromise is to create a `Mutable`, as I do in my answer below, rather than making `T` itself mutable. – Eric Feb 02 '19 at 08:23
0

You can try to create const std::vector<YouType*>. Then you can't change the vector but you can change objects inside vector. But be accurate because you will modify original objects not copies.

Use smart pointers or raw pointers depends on your use cases: you have owning vector or just vector of observers.

Eric
  • 95,302
  • 53
  • 242
  • 374
Gusev Slava
  • 2,136
  • 3
  • 21
  • 26
  • 1
    _"but you can change objects inside vector"_ - [You're wrong](https://godbolt.org/z/HDvDuR). That's how I'd like it to work, but `vector` wasn't designed that way, so that's not how it works. – Eric Feb 02 '19 at 08:12