Class conversion not working as expected - c++

I defined simple conversion method
operator string() { return /*...*/; }
When I call it directly
obj.operator string()
It works fine, but when I call it this way ...
(string)obj
Result is empty string.
What's going on? (I'm using gcc c++14) (Can post code If needed)
Class
class String : public std::string {
std::string str_;
public:
String() {};
String(const String & s) {
str_ = std::string(s.str_);
};
String(String && s) {
str_ = std::string(s.str_);
};
String(const string & s) {
str_ = std::string(s);
};
String(const char * s) {
str_ = std::string(s);
};
char & operator[](size_t i) {
return str_[i];
};
String & operator=(const String & str) {
if (this != &str) {
str_ = str.str_;
}
return *this;
};
String & operator=(String && str) {
if (this != &str) {
str_ = str.str_;
}
return *this;
};
bool operator==(const String & str) {
return str_ == str.str_;
};
bool operator!=(const String & str) {
return str_ != str.str_;
};
operator string() {
return str_;
};
};

The problem is that your class is deriving from std::string.
Remove the : public std::string part and the conversion operator will be used.
You class is already declaring an std::string member (so it uses the "has-a" approach) and doesn't need the "is-a" approach.
By the way deriving from standard library classes is almost always a bad idea (they were not designed for that).

Related

const char* name’ previously declared

class MyString:public string
{
public:
MyString(){ string();}
MyString(const char* name){
string(name);
}
MyString(const MyString& a){
*this = a;
}
MyString(const string& a):string(a){}
MyString operator()(int start,int end){
MyString ret(substr(start,end));
return ret;
}
};
when I write this, it shows that
‘const char* name’ previously declared here
10 | MyString(const char* name){
| ~~~~~~~~~~~~^~~~
and string(name);
|
what should I do?
just like words written above
If you really want to write your own string class based in the standard string class then the way to do it is to use composition not inheritance. Based on the code written above, something like this
class MyString
{
public:
MyString() {}
MyString(const char* name) : my_string(name) {}
MyString(const std::string& name) : my_string(name) {}
MyString operator()(int start, int end) const {
return my_string.substr(start, end);
}
private:
std::string my_string;
};

How to create operator overload?

I have code like below and I need to write a global function overloading the addition operator for objects of this class so that the resulting object represents the concatenation of two strings separated by the '+' sign. Can someone help me?
class A {
char* str;
// ...
};
Such operator should have access to content of class and to create a new instance of class A which it returns. Assuming there is no public interface to access str such operator have to be a friend function with signature similar to one below
class A {
char* str;
// ...
friend A operator+ (const A& arg1, const A& arg2)
{
A temp{arg1}; // assuming that A have a copy constructor
// perform concatenation of temp and arg2 here
return temp;
}
};
Try something like this:
class A
{
char* str;
// ...
public:
A(const char *s = nullptr) : str(nullptr) {
if (s) {
str = new char[strlen(s)+1]);
strcpy(str, s);
}
}
A(const A &src) : A(src.str) {}
A(A &&src) : str(src.str) { src.str = nullptr; }
~A() { delete[] str; }
A& operator= (A rhs) {
A temp{std::move(rhs)};
std::swap(str, temp.str);
return *this;
}
friend A operator+ (const A& arg1, const A& arg2)
{
A temp;
temp.str = new char[strlen(arg1.str)+1+strlen(arg2.str)+1];
sprintf(temp.str, "%s+%s", arg1.str, arg2.str);
return temp;
}
};
That being said, you really should use std::string instead of char*:
class A
{
std::string str;
// ...
public:
A(const std::string &s = "") : str(s) {}
friend A operator+ (const A& arg1, const A& arg2)
{
return A{arg1.str + "+" + arg2.str};
}
};

Correct way to write operator+ using rvalues

I've been writing my own String class and I am not sure how to write operator+ correctly considering I could pass rvalues into it.I think I should have the following 3 non-member functions
String operator+(String &&lhs, String &&rhs);
String operator+(String& lhs,String&&rhs);
String operator+(String&&lhs,String&rhs);
However I am not sure how to implement them. Any help would be appreciated.
First, make sure to define copy and move constructors in your String class:
class String
{
private:
char *m_data;
std::size_t m_length;
...
public:
String();
String(const String &src);
String(String &&src);
~String();
...
};
String::String() :
m_data(nullptr),
m_length(0)
{
}
String(const String &src) :
m_data(new char[src.m_length+1]),
m_length(src.m_length)
{
std::copy_n(src.m_data, m_length, m_data);
m_data[m_length] = 0;
}
String(String &&src) :
m_data(nullptr),
m_length(0)
{
std::swap(m_data, src.m_data);
std::swap(m_length, src.m_length);
}
String::~String()
{
delete[] m_data;
}
Then define operator+ and operator+= for the class:
class String
{
public:
...
String& operator+=(const String &rhs);
...
friend String operator+(String lhs, const String &rhs)
{
lhs += rhs;
return lhs;
}
};
String& String::operator+=(const String &rhs)
{
String tmp;
tmp.m_length = m_length + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(m_data, m_length, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + m_length);
tmp.m_data[tmp.m_length] = 0;
std::swap(m_data, tmp.m_data);
std::swap(m_length, tmp.m_length);
return *this;
}
By taking a const String & as input on the right side, that will handle both lvalue and rvalue inputs.
For operator+, the left-hand side is taken by value so the compiler can decide the best constructor to use based on whether the input is an lvalue (copy) or rvalue (move).
Alternatively, you can implement it to take const String & on the left side so it still handles lvalues and rvalues, but then you have to implement it similar to how operator+= is implemented to avoid the extra allocation of copying lhs before concatenating onto it:
friend String operator+(const String &lhs, const String &rhs)
{
/*
String tmp(lhs);
tmp += rhs;
return tmp;
*/
String tmp;
tmp.m_length = lhs.m_length + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(lhs.m_data, lhs.m_length, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + lhs.m_length);
tmp.m_data[tmp.m_length] = 0;
return tmp;
}
Either way, you should also define a conversion constructor and operator+ for const char * input as well:
class String
{
public:
...
String(const char *src);
...
friend String operator+(const char *lhs, const String &rhs)
{
return String(lhs) + rhs;
/* or:
std::size_t len = std::strlen(lhs);
String tmp;
tmp.m_length = len + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(lhs, len, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + len);
tmp.m_data[tmp.m_length] = 0;
return tmp;
*/
}
...
};
String::String(const char *src) :
m_data(nullptr),
m_length(std::strlen(src))
{
m_data = new char[m_length+1];
std::copy_n(src, m_length, m_data);
m_data[m_length] = 0;
}
This will allow concatenating String objects with string literals (String + "literal", "literal" + String, String += "literal", etc).
See operator overloading on cppreference.com for more details.
The way I usually do it is like this:
class foo
{
...
public:
...
foo&& operator +(foo const & other) &&;
foo&& operator +(foo && other) const &;
foo&& operator +(foo && other) &&;
foo operator +(foo const & other) const &;
};
Not sure if Microsoft supports this but this is a good way to do this in more recent standards. Try clang if msvc wont let you.
The advantages of doing it this way are that you get very fine levels of control over the method used. These 4 operations can also be defined outside of the class if needed. But you'll always want 4 for the 4 possibilities of r-value/l-value combinations.
Also, you generally want to qualify l-values as const to indicate that they are not modified.
Simply defining a copy/move constructor is not usually an efficient solution to this problem. You will need a good understanding of how rvalue references work to implement this efficiently.

Why is this assert failing?

The assert in main.cpp is failing, and I don't understand why.
Here is string.hpp
class String
{
private:
int len;
char* str;
public:
String(char const* s); // C-string constructor
~String() {delete str;}; // destructor
char* const getString(); //get string for printing
};
inline bool operator==(String lhs, String rhs)
{
return std::strcmp(lhs.getString(),rhs.getString());
}
// Define operator!= in terms of ==
inline bool operator!=(String const& lhs, String const& rhs)
{
return !(lhs == rhs);
}
and here is string.cpp
String::String(char const* s) // C-string constructor
{
len = std::strlen(s);
str = new char[len+1];
std::strcpy(str,s);
}
char* const String::getString()
{
return str;
}
and here is main.cpp
#include <cassert>
int main()
{
String c = "c";
String d = "d";
assert(c == c);
assert(c != d);
}
I tried to include only the essential code. I left out a lot of the obvious includes. The assert(c == d) is failing and I don't understand why. The operator overload of == should have returned a true result.
std::strcmp returns 0 if the strings are equal. So your operator== will return false for equal strings and true else.
You could, for instance, switch the implementations of == and != around,
strcmp returns 0 when its arguments have equal contents.
So add a comparison with 0 to your operator==:
inline bool operator==(String const& lhs, String const& rhs)
{
return std::strcmp(lhs.getString(), rhs.getString()) == 0;
}
Also, since you probably don't want to copy the arguments each time you call operator==, I'd recommend passing them by reference.

std::map key not found even though the entries are identical

I'm learning C++ and I've been writing a wrapper for std::map and std::string, and I've stumbled upon a problem. Whenever I add something to the map using a string as key, once I try to access that item using the exact same key it says the key is out of bounds of the map. Here's my code (irrelevant parts left out):
ADictionary.h
#ifndef ADICTIONARY_H
#define ADICTIONARY_H
#include <map>
...
template<typename KEY, typename VALUE>
class ADictionary {
public:
...
VALUE operator [](KEY key) const {
return value.at(key);
}
void add(KEY key, VALUE value) {
this->value.insert(std::make_pair(key, value));
}
...
private:
std::map<KEY, VALUE> value;
};
#endif
AString.cpp
#include "AString.h"
AString::AString() {
value = "";
}
AString::AString(const char character) {
value = character;
}
AString::AString(const char * characters) {
value = characters;
}
AString::AString(std::string text) {
value = text;
}
...
AString::operator const char *() const {
return value.c_str();
}
AString::operator const std::string() const {
return value;
}
...
ABoolean AString::operator<(AString & text) const {
return getLength() < text.getLength();
}
ABoolean AString::operator>(AString & text) const {
return text < *this;
}
ABoolean AString::operator==(AString & text) const {
return value == text.value;
}
ABoolean AString::operator!=(AString & text) const {
return !(text == *this);
}
AString & AString::operator=(AString & text) {
value = text.value;
return *this;
}
...
The code which uses the above
ADictionary<AString, AString> test;
AString a = "a";
AString b = "b";
test.add(a, b);
std::cout << test[a]; // Error occurs here, according to the program "a" is not a key in the map
I hope someone can explain to me what's going wrong. I've tried creating a dictionary with the default std::string as types and it worked correctly:
ADictionary<std::string, std::string> test;
std::string a = "a";
std::string b = "b";
test.add(a, b);
std::cout << test[a]; // No error this time
As I've said, I'm pretty new to C++ so there may be other errors. If so, feel free to point them out.
Thanks!
EDIT:
AString.h
#ifndef ASTRING_H
#define ASTRING_H
#include <string>
#include "ABoolean.h"
#include "AInteger.h"
#include "AList.h"
class ABoolean;
class AInteger;
template<typename VALUE>
class AList;
class AString {
public:
AString();
AString(const char);
AString(const char *);
AString(std::string);
~AString();
operator const char *() const;
operator const std::string() const;
operator const AInteger() const;
ABoolean operator<(AString &) const;
ABoolean operator>(AString &) const;
ABoolean operator==(AString &) const;
ABoolean operator!=(AString &) const;
AString & operator=(AString &);
AString & operator+(AString &);
AString & operator+=(AString &);
void clear();
ABoolean contains(AString) const;
AInteger getIndex(AString) const;
AInteger getLength() const;
AList<AString> getSplit(AString) const;
AString getSubstring(AInteger, AInteger) const;
void removeRange(AInteger, AInteger);
void removeSubstring(AString);
void toLowercase();
void toUppercase();
private:
std::string value;
};
AString & operator+(const char, AString &);
AString & operator+(const char *, AString &);
#endif
Your string operators appear to be incorrect.
std::map uses the less than operator by default. While you provide one for AString, the only thing it does is check the length of the string. What if the two strings are of equal length?
The correct thing to do is to lexicographically compare the characters in the string. While there is a standard library function to do this, you can use operator < of the std::string values in your class:
friend bool operator<(AString const& a, AString const& b)
{
return a.value < b.value;
}
EDIT: You may also wish to remove your conversion operators, or at least make them explicit, which prevents surprising and unwanted implicit conversions. Constructors taking one parameter (other than copy or move constructors) should also be declared explicit.