How to have non-const access to object held by a container when the container is const in C++

StackOverflow https://stackoverflow.com/questions/14249595

  •  14-01-2022
  •  | 
  •  

문제

I have a relationship between two classes and some additional functional code illustrated in the below example. The MiddleMan class holds a couple of containers of pointers to DataObject class instances. I want to enforce read-only access to the data objects in one container, while allowing write access to the data containers in the other. Users of MiddleMan should never be able to modify the containers themselves directly (the ptr_vector members).

I guess there is const promotion happening to the DataObject pointers when accessed through the MiddleMan const member function. How can I avoid this? In my object relationship, MiddleMan does not logically own the DataObject instances. Thus, I do not want the DataObject instances const-protected.

I have used the boost::ptr_vector container as I understand the STL containers can be more problematic for this sort of thing. Didn't solve my problem though. I am happy to use const_cast in MiddleMan, but I don't see how to.

// const correctness access through middleman example
// g++ -I/Developer/boost  ptr_container_ex.cc
#include <boost/ptr_container/ptr_vector.hpp>

struct DataObject
{
    DataObject(size_t n) : _data(new float[n]) {}
    ~DataObject() {delete _data;}
    float * dataNonConst() { return _data; }
    const float * dataConst() const { return _data; }
    float * _data;
};


struct MiddleMan
{

    typedef boost::ptr_vector<DataObject> containerVec_t;

    const containerVec_t & inputVars() const { return _inputVars; }
    const containerVec_t & outputVars() const { return _outputVars; }

    void addInputVar(  DataObject * in  ) { _inputVars.push_back( in ); }
    void addOutputVar( DataObject * out ) { _outputVars.push_back( out ); }

    containerVec_t _inputVars, _outputVars;

};

// just an example that the DataObject instances are managed externally
DataObject g_dataInstances[] = {DataObject(1), DataObject(2), DataObject(3)};

MiddleMan theMiddleMan;


int main()
{
    theMiddleMan.addInputVar( &g_dataInstances[0]);  // this is just setup
    theMiddleMan.addOutputVar( &g_dataInstances[1]);

    const MiddleMan & mmRef = theMiddleMan; // I actually only have a const ref to work with

    // read data example
    const MiddleMan::containerVec_t & inputs = mmRef.inputVars();
    float read = inputs[0].dataConst()[0];

    // write data example
    const MiddleMan::containerVec_t & outputs = mmRef.outputVars();
    float * data_ptr = outputs[0].dataNonConst(); // COMPILER ERROR HERE:

    return 0;
}

I'm getting the compiler output:

ptr_container_ex.cc: In function ‘int main()’:
ptr_container_ex.cc:49: error: passing ‘const DataContainer’ as ‘this’ argument of ‘float* DataContainer::dataNonConst()’ discards qualifiers
도움이 되었습니까?

해결책 2

One working scenario uses const_cast in accessing the outputVars. I'm not able to return a reference to the whole container, but I can get the functionality I need returning begin and end iterators.

// const correctness access through middleman example
// g++ -I/Developer/boost  ptr_container_ex.cc
#include <boost/ptr_container/ptr_vector.hpp>

struct DataObject
{
    DataObject(size_t n) : _data(new float[n]) {}
    ~DataObject() {delete _data;}
    float * dataNonConst() { return _data; }
    const float * dataConst() const { return _data; }
    float * _data;
};


struct MiddleMan
{
    typedef boost::ptr_vector<DataObject> containerVec_t;

    containerVec_t::iterator outputVarsBegin() const { return const_cast<containerVec_t&>(_outputVars).begin(); }
    containerVec_t::iterator outputVarsEnd() const { return const_cast<containerVec_t&>(_outputVars).end(); }

    const containerVec_t & inputVars() const { return _inputVars; }

    void addInputVar(  DataObject * in  ) { _inputVars.push_back( in ); }
    void addOutputVar( DataObject * out ) { _outputVars.push_back( out ); }

    containerVec_t _inputVars, _outputVars;

};

// just an example that the DataObject instances are managed externally
DataObject g_dataInstances[] = {DataObject(1), DataObject(2), DataObject(3)};

MiddleMan theMiddleMan;


int main()
{
    theMiddleMan.addInputVar( &g_dataInstances[0]);  // this is just setup
    theMiddleMan.addOutputVar( &g_dataInstances[1]);

    const MiddleMan & mmRef = theMiddleMan; // I actually only have a const ref to work with

    // read data example
    const MiddleMan::containerVec_t & inputs = mmRef.inputVars();
    float read = inputs[0].dataConst()[0];

    // write data example
    float * data_ptr2 = mmRef.outputVarsBegin()->dataNonConst(); // WORKS

    return 0;
}

다른 팁

You need to exclude const modifiers every place you are not going to have const access:

containerVec_t & outputVars() { return _outputVars; } //Change definition in MiddleMan class

MiddleMan::containerVec_t & outputs = theMiddleMan.outputVars(); //Change reference type
float * data_ptr = outputs[0].dataNonConst(); //No compiler error here
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top