Const vector of non-const objects

2019-03-17 09:57发布

问题:

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.

回答1:

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?



回答2:

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 )


回答3:

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.



回答4:

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;
};


回答5:

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.



回答6:

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.