Overloading subscript operator not working as expected - c++

I have a String struct that I overloaded the subscript operator on. But it doesn't seem to work.
//my_string.h
struct String {
char* Text;
uint64 Length;
char& operator[](int32 index);
}
//my_string.cpp
char& String::operator[](int32 index) {
ASSERT(index >= 0);
return Text[index];
}
//main.cpp
String* test = string_create("Hello world");
char c = test[0];
Visual Studio gives me the following error:
no suitable conversion function from "String" to "char" exists

The compiler issued an error because in this statement
char c = test[0];
the expression test[0] has the type String.
In this declaration
String* test = string_create("Hello world");
you declared a pointer instead of an object of the type String.
If it is not a typo then in this case you have to write
char c = ( *test )[0];
or
char c = test->operator []( 0 );
Also it looks strange that the data member Length has the type uint64
uint64 Length;
while the index used in the operator has the type int32.

Related

Error when converting to char * to my custom class(MyString) object

I am trying to write my own string class using the library for preparing my exam .But I had this error saying that
main.cpp:9:22: error: no match for ‘operator+’ (operand types are ‘const
char [5]’ and ‘MyString’)
MyString c = "Hola" + b;
My main is like this. It works file then "Hola" and b change place.
MyString b("Mundo\n");
MyString c = "Hola" + b;
I think it doesn't call the constructor there .
My class has these in private.
char * _str
int _length
My constructor.
MyString::MyString(const char * str){
int length = 0;
for(char c = str[0]; c != '\0' ; c++)
++length;
_length = length;
_str = new char[length+1];
strcpy(_str,str);
}
And my + overload
const MyString MyString::operator+(const MyString& mS) const{
char * tempChar = new char[_length + mS._length];
MyString tempStr(tempChar);
delete[] tempChar;
strcpy(tempStr._str,_str);
strcat(tempStr._str,mS._str);
return tempStr;
}
Edit : I solved it by making operator+ a friend function but I want to know why
when you write
MyString b{"Mundo\n"};
MyString c = b + "Hola";
string literal will be sent your operator+ function paremeter, and MyString object created with "Holla"(You can test it with write a text in your constructor).
Besides, when you didn't write your friend function and write
MyString b{"Mundo\n"};
MyString c = "Holla" + b;
compiler will search function which has first parameter is const char * and can't find.(and no match error show this). this is the reason for writing friend function.
I hope you can understand reason

Why initialization of const char array member incompatible in constructor initializer?

I have a Test class with overloaded constructor. Initializing const char array member by string literals work fine. But, initialization by const char * gives error -
error: incompatible types in assignment of ‘const char*’ to ‘const
char [25]’
class Test
{
const char d_arr[25];
public:
Test() : d_arr("Test Class") {}
Test(const char * arr) : d_arr(arr) {}
};
How to resolve this?
You are assigning a pointer to an array, which is not allowed.
i.e. You cannot do following:
const char *arr = "ABC";
const char d_arr[25] = arr;
What you need to do is copy the chars manually i.e. something like:
Test(const char * arr) {
size_t index = 0;
if (arr) {
while (arr[index] && index < 24) {
d_arr[index] = arr[index];
++index;
}
}
d_arr[index] = 0;
}
That all said, as said in comments, its better to use std::string.

C++ plus operator not working with const char and others

Hello i made a variable storage like string and this is the source
class str
{
private:
const char * _mystring;
public:
// Using this function to made new str
str(const char * _str) { _mystring = _str; }
// Get the content of this str
const char * read() { return _mystring; }
// Get the content in char *
char * read_c() { return strdup(_mystring); }
// Operator for equal action with extern const char
str & operator = (const char * _str) { _mystring = _str; return *this; }
// Operator for equal action with extern str storage
str & operator = (str _str) { _mystring = _str.read(); return *this; }
// Operator for add action
str operator + (const char * _str) {
return str(strcat(read_c(), _str));
}
// Operator for add action with new str
str operator + (str & _str) {
return str(strcat(read_c(), _str.read()));
}
};
and this is my test program
int main() {
str var1("Var1"); // No problem
str var2("Var2"); // No problem
str var3 = var1 + var2; // No problem
str var4 = var1 + "tempStr"; // I got runtime error !
str var5 = "tempStr" + var2; // I got error before compile
cout << var1.read() << endl;
cout << var2.read() << endl;
cout << var3.read() << endl;
cout << var4.read() << endl;
return 0;
}
what is the problem that i can't create something like var3 and var4 and i got error (i get error while i want to merge const char with my str ... i set + operator but there is problem for when i want to merge const char with my str (not my str with const char... on this action, there is no problem)
but wait i got error (debug (close program)) after compile and print var7 too !
For str var3 = "tempTxt0" + "tempTxt1" + "tempTxt2"; // Error:
This does not work because the compiler sees these types (omitting variable names): str = const char* + const char* + const char *. Adding char pointers together is not what you want. However, if you only had one of them to be type of class str, it would work. E.g. str = str(const char*) + const char* + const char * would call the str::operator+, which returns a new object of type str and the rest will follow the same way.
For str var4 = "tempTxt3" + var1; // Error:
This does not work because you declare operator+ as a member function. Again compiler sees these types (omitting variable names): str = const char* + str. There is no operator+ declared for const char* as the first operand and str as the second operand. You have a member operator+ but that operator works on a class str so you would need a class str on the left hand side; as in str + const char*. Think of it like a function call because it is exactly a member function call. It is the same as typing str = str.operator+("text"). Now you see, str = "text".operator+(str) does not work because it does not exist.
If you declared a non-member function str operator+(const char* first, const str& second);, it would work. The compiler will look for operators that matches what you want in outer namespace scopes of the parameters up to the global scope (this is called ADL). But there is a better way:
Declare str operator+(const str& lhs, const str& rhs);. If you declare your operators this way in the namespace scope of the class str, then compiler will use ADL (on Wikipedia) using the second argument's type (which is class str) and find this operator. Then, the compiler can see that class str has a non-explicit constructor which can take const char*: str(const char * str). It can then create a temporary object of class str and use the operator+ you defined.
So for your example, as you do not have any namespaces, you would need this at its simplest (notice I have added a few const keywords):
class str
{
private:
const char * _mystring;
public:
// Using this function to made new str
str(const char * _str) { _mystring = _str; }
// Get the content of this str
const char * read() const { return _mystring; }
// Get the content in char *
char * read_c() const { return strdup(_mystring); }
// Operator for equal action with extern const char
str & operator = (const char * _str) { _mystring = _str; return *this; }
// Operator for equal action with extern str storage
str & operator = (str _str) { _mystring = _str.read(); return *this; }
};
// Operator for add action with new str
str operator+ (const str& lhs, const str& rhs) {
return str(strcat(lhs.read_c(), rhs.read()));
}
P.S.
Your example has more problems. read_c duplicates the string (let say) of 10 bytes and you concatenate more bytes at the end of it. That is an overflow because your duplicated buffer is 10 bytes. You have to allocate and free the memory properly but that is for another question I guess. E.g.
str operator+(const str& lhs, const str& rhs) {
const std::size_t ls = lhs.size();
const std::size_t rs = rhs.size();
char* n = new char[ls + rs + 1];
std::memcpy(n, lhs.read(), ls);
std::memcpy(n + ls, rhs.read(), rs);
n[ls + rs] = '\0';
return str(n);
}
Then you still have the problem of when to delete.
str var3 = "tempTxt0" + "tempTxt1" + "tempTxt2"; // Error
str var4 = "tempTxt3" + var1; // Error
In these lines you're trying to add 2 pointers to some static memory (in other words your operator isn't called)
On the left side of the operator plus needs to be your class
Having an empty (only with \0) instance of your class will fix your error (as you might have already noted):
str var4 = str("") + "tempTxt3" + var1; //now it is okay
Also, note that strcat doesn't allocate memory for concatening, you have to do it with realloc yourself
When you override a binary operator such as +, the left operand has to be the object in question. The reason you get errors is because the left operand is a string constant and this the default + is used.
You need to create friend functions for these operators that accept a const char * for the first parameter and either str or a const char * for the second parameter.
str var4 = var1 + "tempStr"; // I got runtime error !
in this case following operator+ overload is called:
str operator + (const char * _str) {
return str(strcat(read_c(), _str));
}
you call strcat on a pointer which points to a buffer not large enough to contain both current string and _str. The fastest solution is to use std::string, otherwise you need to allocate large enough buffer and also remember to keep care of its lifetime (adhere to rule of three etc.)
str var5 = "tempStr" + var2; // I got error before compile
You need a global operator overload of following signature:
str operator + (const char * _str, const str& s);

How can templated code find the length of a string whose type is a template parameter, in C++?

Consider this code:
template <typename T>
class String
{
public:
...
String(T* initStr)
{
size_t initStrLen;
if (initStr != NULL)
{
printf_s("%s\n", typeid(T) == typeid(char) ? "char" : "wchar_t");
if (typeid(T) == typeid(char))
{
strlen((T*)initStr);
}
else if (typeid(T) == typeid(wchar_t))
{
wcslen((T*)initStr);
}
}
}
...
};
When I compiled the code, I got this error message:
...\main.cpp(32) : error C2664: 'strlen' : cannot convert parameter 1 from 'wchar_t *' to 'const char *'
Then I tried to use a function pointer:
typedef size_t (*STRLEN)(void*);
STRLEN _strlen;
_strlen = reinterpret_cast<STRLEN> (typeid(*initStr) == typeid(char) ? strlen : wcslen);
and again the compiler issued an error, this time:
...\main.cpp(28) : error C2446: ':' : no conversion from 'size_t (__cdecl *)(const wchar_t *)' to 'size_t (__cdecl *)(const char *)'
My question is, how can I use the functions strlen and wcslen with templates?
You can do this e.g. by introducing a helper function as illustrated below:
#include <iostream>
#include <string.h>
size_t GetLength(const char* s) { return strlen(s); }
size_t GetLength(const wchar_t* s) { return wcslen(s); }
template <typename T>
void PrintLength(T s)
{
std::cout << GetLength(s) << std::endl;
}
int main()
{
PrintLength("abc");
PrintLength(L"abc");
}
Use this helper function GetLength instead of strlen or wcslen and don't check the type of the argument explicitly. You can write overloads of GetLength for other types as well, e.g. std::string.
You rarely need to use typeid in practice and in this case it is completely inappropriate.
You can't use if statements to control what code is instantiated for a template: all of the code in the body must work for every instantiation.
std::size_t strlen(wchar_t const *s) {
return std::wcslen(s);
}
//...
String(T* initStr) {
using std::strlen; // bring into scope so unqualified call can find it
std::size_t length = strlen(initStr); // uses std::strlen or our strlen
//...
You could also add an overload of your strlen for char, then you don't need the using declaration.
You have misunderstood templates. You should not use typeid to determine types here, but instead use template specialisation.
In case the OP is interested in how strings are implemented in STL, they use a whole helper class call char_traits. This is a class with nothing but static member functions, and char_traits is specialised for char and wchar_t to use the C runtime library functions like memmove.
For example you have a compare function that returns a value <0, 0 or >0. Where the type is char it can use memcmp. Where the type is wchar_t it can use the wide equivalent.
It works something like this:
template< typename Element >
class char_traits
{
public:
static int compare( const Element * left, const Element * right, size_t length )
{
for( const Element * end = left + length; left != end; ++left )
{
if( left < right )
return -1;
else if( left > right )
return 1;
}
return 0;
}
// other functions
};
template <> class char_traits<char> // I think this is the syntax
{
public:
int compare( const char * left, const char * right, size_t len )
{
return memcmp( left, right, len ); // more efficient than the general loop above
}
// other functions
};
// specialise also for wchar_t

C++ "conversion loses qualifiers" compile error

I ran into an interesting problem while debugging SWIG typemaps today. Anyone care to enlighten me why Visual C++ 2008 throws a "conversion loses qualifiers" error when converting from ourLib::Char * to const ourLib::Char * &? I thought Type * -> const Type * was a trivial conversion, and (when calling functions) Lvalue -> Lvalue & as well.
EDIT: The solution we ended up going with:
// ourLib::Char is a typedef'ed char on Win32
%typemap(in) const char* (const ourLib::Char* tmp)
{
if (!bapiLua::LuaTraits<ourLib::Char*>::FromLuaObject(L, $argnum, tmp)) SWIG_fail;
$1 = const_cast<char *>(tmp);
}
// And in a different source file, already written:
namespace bapiLua {
template<>
struct LuaTraits<ourLib::Char*>
{
static ourLib::Bool FromLuaObject(lua_State* L, int pos, const ourLib::Char*& o_result);
};
}
Removing the const from const ourLib::Char * tmp causes the error I described.
Say you had the following function:
void test( const char*& pRef)
{
static const char somedata[] = { 'a' ,'b', 'c', '\0'};
pRef = somedata;
}
If you passed in a non-const char*, then when test() returned the compiler would have lost the fact that what p is pointing to is const.
It's essentially the same reason as given in this C++ FAQ Lite question (dealing with pointers-to-pointers rather than pointer references):
http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.17
In following code,
friend ostream & operator<<(ostream & output, const List& other)
{
for(int i=0;i<other.length();i++)
output << other.getData()[i] << " ";
return output;
}
to remove compilation error "Conversion loses qualifiers", for parameter "const List& other", I changed both following called methods to const.
T* getData() const
{
return data;
}
int length() const
{
return lSize;
}