Overloading operator and modifiyng string - c++

I am learning about operator overlaoding. I have created simple class to test it.
class Beer{
public:
Beer(int oner , int twor , string name){
this -> one = oner;
this -> two = twor;
this -> name = name;
};
int getOne(){
return this -> one;
};
int getTwo(){
return this -> two;
};
string getName(){
return this -> name;
};
Beer operator + (const Beer &a)const {
return Beer(5,two+a.two,"firstName");
};
Beer operator + (string a)const {
this -> name = this -> name +" "+a;
};
private:
int one;
int two;
string name;
};
I am trying to figure out , how to midify the string with overloaded operand. My function i declared
Beer operator + (string a)const {
this -> name = this -> name +" "+a;
};
Throws error about passing const string.
I tried using
Beer operator + ( const string *a)const {
swap(this -> name , this -> name + " " + a);
return *this;
};
Which complained about one being cosnst string , and secon one being basic string.
The idea is simple.
Beer one ( 5, 6, "one")
one + "two"
// one.name = "one two"
What is the right way how to do it?
// error with swap
error: no matching function for call to 'swap(const string&, std::basic_string<char>)'|
// erro with string
passing 'const string {aka const std::basic_string<char>}' as 'this' argument of 'std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(std::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]' discards qualifiers [-fpermissive]|

Comments:
Don't include the entire std namespace. You're likely to run into nasty name clashes with your own code. At most, use the symbols you need explicitly, e.g. using std::string;.
Unless you need a copy of a value to modify, pass large objects like std::string by const reference. When you declare a parameter as having a value type std::string, you receive a copy of the string, and that's expensive unless you need a copy to modify inside of your function.
This is a long-standing problem with the C++ standard: an implementation detail like this, that should be irrelevant to the user of the function, leaks into the interface (the declaration of the function). Still, when having a copy makes sense, let the compiler give you one without having to type as much. Thus:
// prefer this
std::string fooize(std::string foo) {
assert(foo.size() > 0);
foo.insert(1, "foo");
return foo;
}
// over this
std::string fooize(const std::string & bar) {
assert(bar.size() > 0);
auto foo = bar;
foo.insert(1, "foo");
return foo;
}
Use an initializer list, you then won't need to do silly name gymnastics (you had oner, twor names:
Beer(int one, int two, const std::string & name) :
one(one),
two(two),
name(name)
{}
Declare read-only accessors const:
int getOne() const { return one; }
Return large values like strings by const reference; the user code will likely have the compiler help out with making a copy when needed automatically:
const std::string & getName() const { return name; }
// use:
Beer beer{0,0,""};
std::cout << (beer.getName() + "!") << std::endl; // makes a copy of name as needed
In the + operator taking a string, you're supposed to return a new object, not modify this. You pretty much should do it the way the other operator + you have did it.
Beer operator +(const std::string & a) const {
return Beer(one, two, name + " " + a);
};
If you want to modify your object, you want operator +=:
Beer & operator+=(const std::string & a) {
name += " ";
name += a;
return *this;
}
Even though your class was designed to experiment with operators, you should always consider whether the operators make life easier or not. For example, you class has three members. It's not immediately apparent which of these members would be operated on, unless it was otherwise clear from the class's semantics. It'd be much clearer to have methods named addToOne, addToTwo, and appendToName, for example, instead of operator(s), or simply letting the user set the member through setters, like setOne(int one) { this->one = one; }. The user would then simply do beer.setOne(beer.getOne() + 2);.
Consider naming getters without the get prefix, e.g.
class Beer {
int m_one;
public int one() const { reeturn m_one; }
};
It's less typing for the user. The standard library, as well as large libraries like boost and Qt follow this convention, e.g. you have std::string::size(), not std::string::getSize(), etc.

Beer operator + (string a)const {
this -> name = this -> name +" "+a;
};
You should not change the contents of the object who's + operator is beeing called, afterall if you perform A = B + C, the contents of B should not change. The compiler is correctly informing you about this because it is a const function.
Rather create a temp object to hold the 'sum' and return it.
Beer operator + (string a)const {
return Beer(one, two, name + " " + a);
};

In your operator+() here:
Beer operator+( string a ) const
{
this->name = this->name + " " + a;
};
the const on the function signature is a guarantee to the compiler that when the function is invoked, you won't change the data in the object, yet you change the data in the object.

Related

Inserting objects into vector using lower_bound with compare struct

I have this class:
class Mail {
public:
Mail(const string & msg) : msg(msg) {}
const string msg;
};
And this structure, which compares two Mail objects:
struct Compare {
bool operator()(const Mail & mail, Mail const & mail2) const {
return mail.msg < mail2.msg;
}
};
I want to have a vector with Mail objects sorted by their message const string msg. However, when I try to insert new object into vector using lower_bound, I get many errors, including:
passing ‘const string as ‘this’ argument discards qualifiers.
int main() {
vector <Mail> mails;
Mail mail2("1");
mails.push_back(mail2);
const string msg = "2";
Mail mail(msg);
auto low = lower_bound(mails.begin(), mails.end(), mail, Compare());
// mails.push_back(mail); // OK
mails.insert(low, mail); // passing ‘const string as ‘this’ argument discards qualifiers
return 0;
}
I dont yet understand const usage much and can't figure out, which const is wrong.
I am sorry, if this has already been asked, but I haven't found an answer to this problem yet.
The errors in C++ can sometimes be hard to diagnose. My tip is to always start at the top and resolve that one first. In this case, there's a long list of them, but they're all really about the same thing -- the assignment operator for Mail cannot be generated.
Think of it this way, the compiler is being helpful and is trying to generate (and inside lower_bound(), use) this function:
Mail& operator=( const& Mail mail )
{
msg = mail.msg;
return *this;
}
But it can't because that assignment in the body is invalid due to msg being const. You can't really write it yourself either since you also can't assign to a const variable.
Usually you don't need member variables to be const because they become const if the instance of the class is itself const:
const auto mail1 = Mail{"1"};
auto mail2 = Mail{"2"};
mail1.msg = "3"; // FAIL! msg is const since mail1 is const
mail2.msg = "4"; // Ok! msg is not const
If you do need a const member, you can't use assignment operators with the class. Them's the breaks.
Remove that const and all works:
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
class Mail {
public:
Mail(const string & msg) : msg(msg) {}
string msg; //////////////////////////////// Not const!
};
struct Compare {
bool operator()(const Mail & mail, Mail const & mail2) const {
return mail.msg < mail2.msg;
}
};
int main() {
vector <Mail> mails;
Mail mail2("1");
mails.push_back(mail2);
const string msg = "2";
Mail mail(msg);
auto low = lower_bound(mails.begin(), mails.end(), mail, Compare());
// mails.push_back(mail); // OK
mails.insert(low, mail); // OK!
return 0;
}
See it run live on Coliru.
Footnotes:
You could use a lambda for the comaparator to avoid some boilerplate around the class Compare:
const auto low = lower_bound( begin(mails), end(mails), mail,
[]( const auto& mail1, const auto& mail2 )
{ return mail1.msg < mail2.msg; } );
You can use vector::emplace_back() to construct items in place, avoiding a copy. The following blocks do the same thing in effect, but the second is more efficient:
const auto mail = Mail{"2"};
mails.push_back( mail2 ); // Copies
mails.emplace_back("2"); // Creates it right in the vector
Consider using vector::reserve() if you know how many items you'll put in your vector.
The problems here pertain to the deleted copy assignment operator and the deleted move assignment operator because of the const string msg; member in the Mail class:
Deleted implicitly-declared copy assignment operator
A defaulted copy assignment operator for class T is defined as deleted if any of the following is true:
T has a non-static data member of non-class type (or array thereof) that is const;
Deleted implicitly-declared move assignment operator
The implicitly-declared or defaulted move assignment operator for class T is defined as deleted if any of the following is true:
T has a non-static data member that is const;

setting an std::string data member from a derived class constructor

I have a derived class called Mystring that is derived from std::string, and I would like to set the value of the string that I am working with.
From my understanding to access the string object from std::string I would use *this to get the string that I am currently working with.
I would like to set *this to a string of my choosing, I did this my setting *this = n; but it crashes my code and returns a "Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeef3ffff8)" my code is below:
So my question is, how can I set the value of std::string to something through my derived class. Much thanks!
class Mystring : public std::string
{
public:
Mystring(std::string n);
std::string removePunctuation();
std::string toLower();
};
Mystring::Mystring(std::string n)
{
*this = n;
}
std::string Mystring::removePunctuation()
{
long int L = length();
char *cstr = new char[L + 1];
strcpy(cstr, c_str());
//cout << cstr[L-1] << endl; // last character of c string
if(!isalpha(cstr[L-1]))
{
pop_back() ;
}
return *this;
}
std::string Mystring::toLower()
{
long int L = length();
char *cstr = new char[L + 1];
strcpy(cstr, c_str());
for(int i = 0; i < L;i++)
{
int buffer = cstr[i];
cstr[i] = tolower(buffer);
std::cout << cstr[i];
}
std::string returnstring(cstr);
delete [] cstr;
return returnstring;
}
int main() {
Mystring temp("dog");
std::cout << "Hello World";
return 0;
}
Style aside, the fundamental idea of using an assignment operator to "reset" an inherited subobject is not necessarily incorrect.
However, a conversion is required to get from std::string (the type of the RHS) to Mystring (the type of the LHS, i.e. *this). The only way to perform that conversion is to use the constructor Mystring(std::string). Except… you're already in it. Hence that function is effectively recursive and will repeat forever until you exhaust your stack.
You need to upcast *this to a std::string in order to make this work:
static_cast<std::string&>(*this) = n;
I do agree with the other people here that you shouldn't be deriving from std::string, and certainly not just to add a couple of utility functions that ought to be free functions taking std::string (perhaps in a nice namespace, though?).
Don't do it. Derivation provides no benefit in this situation.
Create your added functions as free functions that operate on a string. For example:
void remove_punctuation(std::string &s) {
if (!std::isalpha(s.back()))
s.pop_back();
}
void tolower(std::string &s) {
for (auto &c : s)
c = std::tolower(c);
}
Making either/both of these a member function serves no purpose and provides no benefit.
References
GOTW #84: Monoliths Unstrung
How Non-Member Functions Improve Encapsulation

Meaning of "const -> std::string const&" after the function definition?

Reading the answer for one exercise in C++ Primer, 5th Edition, I found this code:
#ifndef CP5_ex7_04_h
#define CP5_ex7_04_h
#include <string>
class Person {
std::string name;
std::string address;
public:
auto get_name() const -> std::string const& { return name; }
auto get_addr() const -> std::string const& { return address; }
};
#endif
What does
const -> std::string const&
mean in this case?
auto get_name() const -> std::string const& { return name; } is trailing return type notation for the equivalent
std::string const& get_name() const { return name; }
Note that the equivalence is exact in the sense that you can declare a function using one syntax and define it with the other.
(This has been part of the C++ standard since and including C++11).
The part -> std::string const& is trailing return type and is new syntax since C++11.
The first const says it a const member function. It can be safely called on a const object of type Person.
The second part simply tells what the return type is - std:string const&.
It is useful when the return type needs to be deduced from a template argument. For known return types, it is no more useful than than using:
std::string const& get_name() const { return name; }
It all makes more sense when you see an example where it actually matters; as written in the question, it's just an alternative way to declare the return type.
If you have a template function where you can not know the return type in advance, this can actually help. For example:
template <class X, class Y> auto DoSomeThing(X x, Y y) -> decltype(x * y);
You don't know what types X and Y actually are but you know the return value will have the same type as x * y which can be deduced in this way.
const is a cv-qualifier of the usual kind for the member function : *this is const inside the function.
-> std::string const& pairs with auto to form a trailing return type (see (2)). The difference is only syntactic here -- the following syntax is equivalent:
std::string const& get_name() const { return name; }

How do I initialize a string with an object?

So I was wondering if I could do something like the following in C++.
#include <string>
using namespace std;
class foo
{
public:
string bar;
string baz;
foo(const string &faz)
:bar(faz)
{
};
};
int main()
{
const foo foo1("somestring");
bool isTrue = ((std::string(foo1) == "some string");//This should be true
return 0;
}
How would I make std::string(foo1) equal "some string"? Is there some special variable name I need to use? Or is there something else I need to be doing? Please tell me if you know
You've basically got 2 options:
add a conversion operator, so that a foo can implicitly convert to string
define operator== for foo.
The first option may seem attractive at first, but code that uses conversion operators can quickly get out of hand - overload resolution is tricky enough as it is without adding more possible pathways.
The operator== version is quite simple (note, by making it a non-member you allow the char array to appear on the left of the ==):
bool operator==( foo const &f1, foo const &f2 )
{
return f1.bar == f2.bar;
}
You would use it like:
bool isTrue = (foo1 == "some string");
This matches char arrays because foo has a converting constructor, and the implicit conversion chain of char[] -> char * -> const std::string & happens.

Overwrite Cast Operator in C++

As a C++ beginner I want to write some simple type casts. It there a way to create casting logic which can be used in the type new = (type)old format with the prefix parentheses?
string Text = "Hello";
char* Chars = "Goodbye";
int Integer = 42;
string Message = Text + (string)Integer + (string)Chars + "!";
I'd like to stick with this syntax if possible. For example the string cast of boost int Number = boost::lexical_cast<int>("Hello World") has an unattractive long syntax.
Just use a normal function that you overload for different types:
std::string str(int i) {
return "an integer";
}
std::string str(char* s) {
return std::string(s);
}
Then use if not like a cast, but as a normal function call:
string Message = Text + str(Integer) + str(Chars) + "!";
It's the most common thing in C++ to cast using a NAME<TYPE>(ARGUMENT) syntax, like in static_cast<int>(char). It makes sense to extend this the way boost does.
However, if you want to convert non-primitive types, you can use non-explicit constructors with a single argument and the cast operator.
class MyType {
public:
MyType(int); // cast from int
operator int() const; // cast to int
};
This is not possible if you are dealing with already existing types.
You cannot change the behaviour of the C-style cast. C++ will make up its mind how to interpret such a cast.
You could however come up with an intermediate type that shortens the syntax:
template <typename From>
struct Cast {
From from;
Cast(From const& from) : from(from) {}
template <typename To>
operator To() const {
return convert(from,To());
}
};
template <typename From>
Cast<From> cast(From const& from) {
return Cast<From>(from);
};
std::string convert(int, std::string const&);
This would allow you to convert things explicitly but without stating how exactly:
int i = 7;
std::string s = cast(i);