Changing object state in const method - c++

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

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.

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

C++ how to return a const object from a class variable

So I have a class called MusicComposer that has an AudioSignal object as a data member called music. Music is non-const. However, I need to return music as a const AudioSignal in a method called getMusic.
In the source file:
const AudioSignal& MusicComposer::getMusic(){
}
music is declared as:
AudioSignal music;
^ this is in the header file.
I declare music as non-const because music needs to be changed before it is returned by the getMusic method. However, I can't figure out for the life of me how to return a const version of it. How can I return a const version of music?
C++ can automatically change mutable to const for you whenever. No biggie. It's going the other way that's hard.
return music;
If you really want to be explicit you could do this, but it's wierd code, and wierd is bad.
return static_cast<const AudioSignal&>(music);
Also, Cyber observed that getter methods themselves are usually const, which lets them be called even when MusicComposer is const.
const AudioSignal& MusicComposer::getMusic() const {
^^^^^
Take a look in this example:
#include <iostream>
class A { // declare class A
int integer;
};
class B { // declare class B
public:
const A& get() { // get() will return a const ref
return a; // here it is returning the ref of data member a
}
private:
A a;
};
int main() {
A a;
B b;
a = b.get();
return 0;
}
As mentioned by potatoswatter,
const is a property of the expression accessing the object, not the object itself

Return a read-only double pointer

I want to a member variable, which is a double pointer. The object, the double pointer points to shall not be modified from outside the class.
My following try yields an
"invalid conversion from ‘std::string**’ to ‘const std::string**’"
class C{
public:
const std::string **getPrivate(){
return myPrivate;
}
private:
std::string **myPrivate;
};
Why is the same construct valid if i use just a simple pointer std::string *myPrivate
What can i do to return a read-only double pointer?
Is it good style to do an explicit cast return (const std::string**) myPrivate?
Try this:
const std::string * const *getPrivate(){
return myPrivate;
}
The trouble with const std::string ** is that it allows the caller to modify one of the pointers, which isn't declared as const. This makes both the pointer and the string class itself const.
If you want to be really picky :
class C {
public:
std::string const* const* const getPrivate(){
return myPrivate;
}
private:
std::string **myPrivate;
};
There are very rare cases in c++ when a raw pointer (even less for a double pointer) is really needed, and your case doesn't seams to be one of them. A proper way would be to return a value or a reference, like this :
class C{
public:
const std::string& getPrivate() const
{
return myPrivate;
}
private:
std::string myPrivate;
};

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.