In C++, reference as class member shall be const or non-const
-
14-07-2021 - |
Question
Consider the following minimal example.
#include <vector>
class Data
{
std::vector<int>& v;
public:
Data(std::vector<int>& _v) : v(_v) {}
Data(const std::vector<int>& _v) : v(_v) {} // error!
};
int main()
{
std::vector<int> v;
const std::vector<int> c_v;
Data d(v);
Data const_d(c_v);
return 0;
}
This does not compile. The whole output from g++ -Wall
below.
const.cpp: In constructor ‘Data::Data(const std::vector<int>&)’:
const.cpp:8:41: error: invalid initialization of reference of type ‘std::vector<int>&’ from expression of type ‘const std::vector<int>’
The reason is clear to me: The const keyword is upset about my cast in line 8. The issue: I really sometimes need the Data class with std::vector
, but sometimes with const std::vector
. Like having two classes: One for reading into Data
, and one for reading from Data
. However, I do not like to write two Data classes with almost redundant functions. My questions:
- Can you give a nice solution to how I can achieve what I try to do in
main()
? C++11 solutions are pretty welcome, too :) - Is this the reason for
iterator
andconst_iterator
?
Solution
const
-ness of data members needs to be known at compile time. If you "sometimes" need a reference to a const
vector and sometimes non-const
, you should create a class hierarchy with the base abstract class containing the common functionality, and inherit it in two Data
classes: Data
and ConstData
. The Data
would contain a non-const
vector, and ConstData
would contain a const
vector. This way you would not duplicate any logic, while two separate classes would contain two references of different const
-ness.
Here is an example:
class AbstractData {
public:
// Common functions use vect() and const_vect()
void common_function1();
void common_function2();
protected:
virtual vector<int>& vect() const = 0;
virtual const vector<int>& const_vect() const = 0;
};
class Data : public AbstractData {
vector<int>& v;
public:
Data(vector<int>& _v) : v(_v) {}
protected:
vector<int>& vect() const {
return v;
}
const vector<int>& const_vect() const {
return v;
}
};
class ConstData : public AbstractData {
const vector<int>& v;
vector<int> temp;
public:
ConstData(const vector<int>& _v) : v(_v) {}
protected:
vector<int>& vect() const {
return temp; // You can choose to throw an exception instead
}
const vector<int>& const_vect() const {
return v;
}
};
Note that this class hierarchy may need a destructor and various copy constructors, depending on the usage.
OTHER TIPS
The error you got was along the lines of:
foo.cpp: In constructor ‘Data::Data(const std::vector<int>&)’:
foo.cpp:8:44: error: invalid initialization of reference of type ‘std::vector<int>&’ from expression of type ‘const std::vector<int>’
...because you are trying to create a non-const reference to data that you (Data
) promised your caller would be treated as const
.
Here is a solution which declares a const
reference as Data
's member:
#include <vector>
class Data
{
const std::vector<int> &v;
public:
Data(std::vector<int>& _v) : v(_v) {}
Data(const std::vector<int>& _v) : v(_v) {} // error!
};
int main()
{
std::vector<int> v;
std::vector<int> c_v;
Data d(v);
Data const_d(c_v);
return 0;
}
Another option is to use a non-reference member instead:
#include <vector>
class Data
{
std::vector<int> v;
public:
Data(std::vector<int>& _v) : v(_v) {}
Data(const std::vector<int>& _v) : v(_v) {} // error!
};