Working with constant and non-constant functions - C++ - 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.

Related

Best practice for getters with ref-qualifier

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.

How to prevent value assignment with inherited operator[]?

I'm having a custom structure called SortedArrayList<T> which sorts its elements according to a comparator, and I would like to prevent assigning using operator[].
Example:
ArrayList.h
template <typename T> class ArrayList : public List<T> {
virtual T& operator[](const int& index) override; //override List<T>
virtual const T operator[](const int& index) const override; //override List<T>
}
SortedLinkedList.h with following operators
template <typename T> class SortedArrayList : public ArrayList<T> {
public:
SortedArrayList<T>(const std::function<bool(const T&, const T&)>& comparator);
T& operator[](const int& index) override; //get reference (LHS)
const T operator[](const int& index) const override; //get copy (RHS)
}
Test.h
ArrayList<int>* regular = new ArrayList<int>();
ArrayList<int>* sorted = new SortedArrayList<int>(cmpfn);
(*regular)[0] == 5; //allow
(*regular)[0] = 5; //allow
(*sorted)[0] == 7; //allow
(*sorted)[0] = 7; //except
Is this operation possible?
By prevent I mean throwing an exception or something what will warn user to not do it.
Prefer aggregation over inheritance:
template <typename T> class SortedArrayList {
ArrayList<T> m_the_list;
public:
SortedArrayList<T>(const std::function<bool(const T&, const T&)>& comparator);
const T& operator[](const int& index) const {return m_the_list[index];}; // always get const reference
// Can act as a *const* ArrayList<T>, but not as a mutable ArrayList<T>, as that would violate Liskov's substitution principle.
operator const ArrayList<T>&() const {return m_the_list;}
}
As Stephen Newell correctly points out, when you're using inheritance, you're guaranteeing your class SortedArrayList can act as an ArrayList in every possible scenario. This is clearly not the case in your example.
You can read more here about how violating Liskov's Substitution Principle is a bad idea.
You should not do this. It indicates an improper design See the C++ FAQ on Inheritance. Your subclass doesn't fulfill the "is-a" requirement for public inheritance if it can't be used in all ways as the base class (LSP).
If you want to have one type of container that allows member replacement and another that doesn't, then the define the base class that just allows const member access (no need to make it virtual). Then branch from there to MutableList and ImmutableList, and let SortedArrayList derive from Immutable list.
Seems to me like the best practice here would be to implement an at(const int& index) method instead of overloading []. That would be more clear to the user of the interface anyway.
There is a similar function in std::map and other std data structures. For example: http://www.cplusplus.com/reference/map/map/at/
Why do you pass the index as reference at all? Absolutely no need for...
I personally recommend to use unsigned integer types for array indices (what would be the meaning of a negative index anyway???).
const for a type returned by value is (nearly) meaningless - it will be copied to another variable anyway (which then will be modifiable), but you prevent move semantics...
So:
T& operator[](unsigned int index); //get reference (LHS)
T operator[](unsigned int index) const; //get copy (RHS)
(Just some improvement suggestions...)
Now to the actual question: Disallowing modification is quite easy:
//T& operator[](unsigned int index); //get reference (LHS)
T const& operator[](unsigned int index) const; //get copy (RHS)
Just one single index operator, always returning const reference... If user can live with reference, fine, otherwise he/she will copy the value anyway...
Edit in adaption to modified question:
As now inheritance is involved, the stuff gets more complicated. You cannot just get rid of some inherited function, and the inherited one will allow element modification.
In the given situation, I'd consider a redesign (if possible):
class ArrayListBase
{
public:
T const& operator[](unsigned int index) const;
// either copy or const reference, whichever appears more appropriate to you...
};
class ArrayList : public ArrayListBase
{
public:
using ArrayListBase::operator[];
T& operator[](unsigned int index);
}
class SortedArrayList : public ArrayListBase
{
public:
// well, simply does not add an overload...
}
The insertion function(s) might be pure virtual in the base class (where a a common interface appears suitable) or only available in the derived classes. Decide you...

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

Const correctness with reference_wrapper

In various situations I have a collection (e.g. vector) of objects that needs to be processed by a number of functions. Some of the functions need to modify the objects while others don't. The objects' classes may inherit from an abstract base class. Hence, I have something like this:
class A
{
public:
virtual void foo() const = 0;
virtual void bar() = 0;
/* ... */
};
void process_1(std::vector<std::reference_wrapper<A>> const &vec);
void process_2(std::vector<std::reference_wrapper<A const>> const &vec);
Obviously (?) I can't pass the same vector of std::reference_wrapper<A>s to both process_1 and process_2. Solutions I've considered so far include:
Using a C-style cast or reinterpret_cast on a reference to vec
Writing my own reference wrapper that has T& get() and T const & get() const instead of T& get() const
Refactoring with e.g. methods that take a wrapper instead of the vector
Having copies of the vector with and without const
Not using const in reference_wrapper's argument
None of these seems very elegant. Is there something else I could do?
Range adapters.
A range adapter takes a range as input (a container is a range, as it has begin and end returning iterators), and returns a range with different properties.
You'd cast your reference wrappers to the const variant when you dereference the iterator.
boost has iterators that will do this for you (transform iterators), and tools to help write conforming iterators, but it can be done from scratch with some work.
A bit of extra work could even keep the typenames sane.
Even lacking elegance, I would make a reference wrapper:
#include <functional>
template <typename T>
class ReferenceWrapper
{
public:
ReferenceWrapper(T& ref)
: m_ref(ref)
{}
ReferenceWrapper(const std::reference_wrapper<T>& ref)
: m_ref(ref)
{}
const T& get() const noexcept { return m_ref.get(); }
T& get() noexcept { return m_ref.get(); }
operator const T& () const noexcept { return m_ref.get(); }
operator T& () noexcept { return m_ref.get(); }
private:
std::reference_wrapper<T> m_ref;
};
It is a tiny class modeling the original requirements.

A Method that returns "const std::string&" with error checking

I am writing a function, which I want to return "const std::string&". Let's just look at the codes.
class A
{
public:
const std::string& GetString() const
{
if (list.empty())
{
return "Warning!"; // how to get around this line.
}
return list[0];
};
protected:
std::vector<std::string> list;
};
The above codes are an example. The basic idea is to write a function that returns const reference, but also able to check for errors.
So, how to get around "return "Warning!";"?
Thanks,
if (list.empty()) {
static std::string w("Warning!");
return w;
}
since you're returning a const reference, it does not matter that you're always returning a reference to the same object.
If you're interested in using string references, why not take a string reference as a parameter and return a Boolean success/failure flag?
class A
{
public:
const bool GetString(std::string& outString) const
{
if (list.empty())
return false;
outString = list[0];
return true;
};
protected:
std::vector<std::string> list;
};
Achieves same result, and gives a simple Boolean result.
EDIT: as Ferruccio points out, this isn't an approach to be taken lightly. Use of parameters for output in this manner is prone to causing confusion and bugs, and should be used sparingly, and be well documented where used.
If you really want to avoid using exceptions (that would be my first choice), you may consider this:
class A
{
public:
const std::string& GetString() const
{
if (list.empty())
{
return warning;
}
return list[0];
};
protected:
std::vector<std::string> list;
private:
static std::string warning;
};
// in *.cpp
std::string A::warning = "warning";
You might throw an exception in this function and add a function to check whether the list is empty. Then the caller can check before calling GetString(). But if they fail to do that, they get an exception they can't ignore.
If you don't want to use exceptions (there are pros and cons of going either way), I'd first ask if it's reasonable to require the caller to call an "isValid" method first.
class A
{
public:
bool IsStringValid() const
{
return !list.empty();
}
const std::string& GetString() const
{
if (list.empty())
{
return "";
}
return list[0];
};
protected:
std::vector<std::string> list;
private:
static QString warning;
};
If it were me, I'd probably have it return a const std::string* instead so NULL is an option, or define a sentinel value as Fiktik suggested.