Best practice for getters with ref-qualifier - c++

The following code causes undefined behaviour:
class T
{
public:
const std::string& get() const { return s_; }
private:
std::string s_ { "test" };
}
void breaking()
{
const auto& str = T{}.get();
// do sth with "str" <-- UB
}
(because lifetime extension by const& doesn't apply here, as it's my understanding).
To prevent this, one solution might be to add a reference qualifier to get() to prevent it being called on LValues:
const std::string& get() const & { return s_; }
However, because the function now is both const and & qualified, it is still possible to call get() on RValues, because they can be assigned to const&:
const auto& t = T{}; // OK
const auto& s1 = t.get(); // OK
const auto& s2 = T{}.get(); // OK <-- BAD
The only way to prevent this (as far as I can see) is to either overload get() with a &&-qualified variant that doesn't return a reference, or to = delete it:
const std::string& get() const & { return s_; }
const std::string& get() const && = delete; // Var. 1
std::string get() const && { return s_; }; // Var. 2
However, this implies that to implement getter-functions that return (const) references correctly, I always have to provide either Var. 1 oder 2., which amounts to a lot of boilerplate code.
So my question is:
Is there a better/leaner way to implement getter-funtions that return references, so that the compiler can identify/prevent the mentioned UB-case? Or is there a fundamental flaw in my understanding of the problem?
Also, so far I couldn't find an example where adding & to a const member function brings any advantages without also handling the && overload...maybe anyone can provide one, if it exists?
(I'm on MSVC 2019 v142 using C++17, if that makes any difference)
Thank you and best regards

It's somewhat unclear what limitations you're working with. If it is an option, you could get rid of the getter(s), and let lifetime extension do its thing:
struct T
{
std::string s_ { "test" };
};
const auto& str = T{}.s_; // OK; lifetime extended
With getters, you have the options of 1. providing duplicate getters or 2. accept that the caller must be careful to not assume that a reference from getter of a temporary would remain valid. As shown in the question.
You could keep private access while still making lifetime management easy by using shared ownership:
class T
{
std::shared_ptr<std::string> s = std::make_shared<std::string>("test");
public:
// alternatively std::weak_ptr
const std::shared_ptr<const std::string>
get() const {
return s;
}
};
But you must consider whether the runtime cost is worth the easiness.

Related

Removing constness from reference returned by const accessor method versus adding a non-const accessor method

Let's consider the following code:
Class MyVeryAccessibleClass
{
public:
const std::vector<int>& getVect() const {return vect_m;};
private:
std::vector<int> vect_m;
};
Class MyInitClass
{
MyInitClass() : importantInt_m{10}{};
void init();
protected:
int importantInt_m;
};
My project is built in a way that it has to be MyInitClass::init() that initializes or modifies MyVeryAccessibleClass::vect_m.
Some other classes will also have access to MyVeryAccessibleClass::vect_m but I don't want them to be able to modify it, they should only be able to read it.
I thought of 2 solutions:
Casting away the constness of getVect() in init (and only there). This is the solution I am leaning towards, but I've read that const_cast should be avoided so I am not sure this is correct:
MyInitClass::init()
{
MyVeryAccessibleClass* class_l = methodToGetMyVeryAccessibleClassInOneWayOrAnother();
auto vect_l& = const_cast<std::vector<int>&> (class_l ->getVect());
vect_l.push_back(importantInt_m);
}
Adding a non-const accessor to MyVeryAccessibleClass. I don't really like this solution because it means that if in another part of the code I use a pointer to MyVeryAccessibleClass, it will call the non-const accessor, and this accessor should only ever be used in MyInitClass::init()
Class MyClass
{
public:
const std::vector<int>& getVect() const {return vect_m;};
// I've read that I could also call the const accessor from here, but that is not really the point
std::vector<int>& getVect() {return vect_m;};
private:
std::vector<int> vect_m;
};
MyInitClass::init()
{
MyVeryAccessibleClass* class_l = methodToGetMyVeryAccessibleClassInOneWayOrAnother();
auto vect_l& = class_l ->getVect();
vect_l.push_back(importantInt_m);
}
MyOtherClass::doStuff()
{
MyVeryAccessibleClass* class_l = methodToGetMyVeryAccessibleClassInOneWayOrAnother();
// I don't want that.
// I guess one could precise const auto vect_l& to call the const accessor or use the new std::as_const
// But not everybody is familiar with that and I felt like I should not even allow the possibility of making a mistake
auto vect_l& = class_l->getVect();
// Mistake far less obvious than this one but that modifies the vector:
vect_l.push_back(15);
}
Is there a better solution that I didn't think of ? And if not, should I go with solution 1 or solution 2 ? My sentiment was that since I only ever want to modify MyVeryAccessibleClass::vect_m in MyInitClass::init, it was OK to do a const_cast there and not have a non-const accessor in MyVeryAccessibleClass.
You can declare your class MyInitClass as a friend of MyVeryAccessibleClass.
Let's see the code in action.
class MyVeryAccessibleClass
{
friend class MyInitClass;
private:
const std::vector<int> vect_m;
}
Now Let's look at the MyInitClass constructor.
MyInitClass::init()
{
MyVeryAccessibleClass* class_l = methodToGetMyVeryAccessibleClassInOneWayOrAnother();
auto vect_l& = class_l->vect_l;
vect_l.push_back(importantInt_m);
}

Changing object state in const method

I have class that resembles this:
class A{
std::string tmp;
public:
const std::string &doSomething() const{
tmp = "something";
return tmp;
}
};
It is very important the method doSomething() to be const and to return reference instead of the string itself.
Only way I see is to do it with dynamic allocation, e.g. something like:
class A{
MyClass *tmp = new MyClass();
public:
const MyClass &doSomething() const{
*tmp = "something";
return *tmp;
}
};
tmp variable "placeholder" is used ONLY inside doSomething().
Is there some other, clear, better way to do this kind of temp value storage?
You can use mutable modifier on std::string tmp:
class A {
mutable std::string tmp;
...
}
This would allow a const method to modify that member.
Check out the mutable keyword for declaring tmp.
If you try to modify the attribute, you should quit the const qualifier of the method signature:
std::string& doSomething() { ... }
If you don't want to modify it, and you want to ensure that the method returns whatever you are waiting to receive:
const std::string& doSomething() const {...}
returning a const reference is the best way to ensure that the reference doesn't change of value. But without the const qualifier before the return type also should work well because of the second const qualifier (which specifies that the method shouldn't modify the current object)
In conclusion, I'm completely agree with #juanchopanza
Cheers

Refactoring read write accessors from/to one data member

I would like to refactor the accessors in following structure:
template<class T>
class ValueTime {
public:
// accessors for val:
const T& get_val() const { return val; }
template<class V> void set_val(const V& v) { val = v; }
// other accessors for tp
private:
T val;
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
};
I would like to make the accessors to the val data members more useful and intuitive, mostly from the point of view of the "standard/boost user expectations" of such structure representing a "value in time":
template<class V = T> V get_val() { return V(val); }
T& operator*() & { return val; }
const T& operator*() const & { return val; }
Now I can use the accessors this way (see the comments):
int main() {
ValueTime<double> vt;
// get_val() no longer returns const ref and also
// allows explicit cast to other types
std::chrono::minutes period{vt.get_val<int>()}; // I had to use the more pedantic static_cast<int> with the original version
// let's use operator*() for getting a ref.
// I think that for a structure like a ValueTime structure,
// it's clear that we get a ref to the stored "value"
// and not to the stored "time_point"
auto val = *vt; // reference now;
val = 42;
}
Is the getter more usueful now? Do you see anything strange or unsafe or counterintuitive in the new interface (apart from being non backward compatible, which I do not care)?
Furthermore, one doubt I still have is if it's better to implement get_val() by returning V(val) or V{val} or just val. As it is now, it works if V has an explicit constructor. What do you think about this issue?
I personally would advise you to make the interface as descriptive as possible and avoid any convenient conversions to reference of data or similar.
The reason is simply usability and maintenance. If you or somebody else are (re-)visiting code using ValueTime, when you cannot remember the precise interface, you still want to understand your code without re-visiting the definition of ValueTime.
There is a difference to members from std (such as std::vector) is that you know their definition by heart.

Const and non-const access resolves to different overloads?

Let me say we have a simple programming task. But for the sake of clarity I start with few code samples.
First of all we written a some kind of data container class but for the purposes of task no matter what the class is. We just need it to behave const-correct.
class DataComponent {
public:
const std::string& getCaption() const {
return caption;
}
void setCaption(const std::string& s) {
caption = s;
}
private:
std::string caption;
};
Then let us assume we've got a generic class that behaves like facade over arbitrary incapsulated class instance. Say we overloaded member access operator (->).
template <typename T> class Component {
public:
Component() { instance = new T(); }
...
const T* operator-> () const {
return instance;
}
T* operator-> () {
// but there might be additional magic
return instance;
}
private:
T *instance;
};
At this point I should say how I want this to work:
if we're calling non-const member functions of underlying class through member access operator (component->setCaption("foo")) compilier treats non-const T* operator-> () as the best choice.
otherwise if we are trying to call const member functions of underlying class same way (component->getCaption()) compiliers selects const T* operator-> () const on the other hand.
This code sample above won't work this way so I'm curious about possibility to give compiler a behavior like that I have mentioned. Any propositions.
EDIT: Let our member access operator overloaded this way:
const T* operator-> () const { return instance; }
T* operator-> () {
cout << "something going change" << endl;
return instance;
}
And let us have a variable Component<DataComponent> c somewhere. Then on the call to c->getCaption() stdout should remain silent but on the call to c->setCaption("foo") stdout should warn us that something is going to change. VS 2010 compilier makes stdout warn us on each of these calls.
I understand that such semantics suppose that c behaves as const and non-const at the same time. But curiousity is still in my mind.
Whether a const or non-const member is invoked is determined purely by the constness of the object on which it is invoked, not by some subsequent operation. That determination is made before any consideration of the particular method you're invoking in DataComponent. You could still hack up the required functionality less directly using proxy object around DataComponent, with both const and non-const forwarding getCaption()s.
EDIT: details as requested (off the top of my head). You'll need to forward declare some of this stuff - I didn't bother as it makes it even more confusing. Do chip in with any concerns / feedback. Note that this basically assumes you can't / don't want to modify Component for some reason, but it's not a generic templated solution that can simply be wrapped around any arbitrary type - it's very heavily coupled and has a high maintenance burden.
// know they can't call a non-const operation on T, so this is ok...
const T* Component::operator->() const { return instance; }
// they might invoke a non-const operation on T, so...
DataComponent::Proxy Component::operator->() { return DataComponent.getProxy(*this); }
in class DataComponent:
struct Proxy
{
Component& c_;
DataComponent& d_;
Proxy(Component& c, DataComponent& d) : c_(c), d_(d) { }
const std::string& get_caption() const { return d_.get_caption(); }
void set_caption(const std::string& s)
{
c_.on_pre_mutator(d_);
d_.set_caption(s);
c_.on_post_mutator(d_);
}
};
then
DataComponent::Proxy DataComponent::getProxy(Component& c) { return Proxy(c, *this); }
So, this means somewhere you have to hand-code forwarding functions. It's a pain, but if you're doing this for debugging or testing it's not unreasonable. If you're doing this so you can add a lock or something, then there are probably better alternatives.

Working with constant and non-constant functions - C++

I have 3 classes. In it's simplest form, it looks like,
class tree
{
public:
tree_node* find_node(const std::string& text) {
return factory.find(text);
}
private:
tree_node_factory factory;
}
class tree_node
{
public:
tree_node(const std::string& text) : text_(text) {}
const std::string& text() const {
return text_;
}
void set_parent(const tree_node* new_parent);
private:
std::string text_;
}
class tree_node_factory
{
public:
tree_node* find(const std::string& text);
private:
std::vector<tree_node*> allocated_nodes;
}
I don't want to allow users of tree to modify the tree_node returned by methods like find_node. So I changed, find_node and tree_node_factory::find to,
const tree_node* find_node(const std::string& text) const {
return factory.find(text);
}
const tree_node* find(const std::string& text) const;
Problem is tree internally should be able to modify the nodes and work on methods like set_parent. But since factory returns only const nodes, I ended up with adding another overload (non const version) of find into the factory.
tree_node* find(const std::string& text);
I am wondering is this the correct way to handle these kind of problems? I see the code is getting duplicated in the const and non-const versions.
Any thoughts..?
Item 3 in Scott Meyers' book Effective C++ demonstrates a method to remove this code duplication. Basically, in your non-const function you will add const to this, call the const version, then cast the const away. This is safe; though writing to a const-variable leads to undefined behavior, because this was originally non-const it's okay.
Example:
const std::string& operator[](size_t index) const
{
// some other code
// since `this` isn't really const, this is modifiable
return mData[index];
}
std::string& operator[](size_t index)
{
return const_cast<std::string&> // (3) take const off result
(static_cast<const my_type&> // (1) add const
(*this)[index]); // (2) use const version
}
Normally it would all be on one line. You can also make a utility for it.
Note a caveat: if the const version returns a "real" const object, this method clearly results in undefined behavior. The constness of the return value must be reflected by the constness of the object referred to by this. This is broken code:
const std::string& operator[](size_t index) const
{
static const std::string constString = "Don't modify me.";
if (index == 0)
{
// even though `this` isn't really const, this is NOT modifiable
return constString;
}
return mData[index - 1];
}
std::string& operator[](size_t index)
{
return const_cast<std::string&> // (3) !!! take const off result !!!
(static_cast<const my_type&> // (1)
(*this)[index]); // (2)
}
In practice, we avoid global state so this is rarely an issue. It's trivial to check, anyway.
Unfortunately C++ has no tools (besides macros) for eliminating source code duplication in function overloads that look largely identical but differ in constness. You can, however, implement one of the functions using the other and const_cast.