Overloading the Assignment operator for character Array in c++ - c++

It can be the worse questions I am going to ask but I want to overload the Assignment Operator for character Array so basically, I can use it as a string.
This is what I wanted to do.
char c1[]="This";
char c2[10];
c2 = c1;
This is what I have tried so far and yes it didn't work out.
#include<iostream>
using namespace std;
void operator = (char *c1, char *c)
{
for(int idx = 0; c[idx] != 0; idx++)
{
c1[idx] = c[idx];
}
c1[idx] = 0;
}
int main(void)
{
char c[] = "This";
char c1[10];
c1 = c;
return 0;
}

I want to overload the Assignment Operator for character Array
This is not possible. The C++ language does not allow defining operator overloads to anything other than classes.
Some alternatives:
Wrap the array as a member of a class and define assignment operator for that class. This is what std::array is, although it uses the implicit assignment operator which behaves differently from your operator (you cannot assign arrays of different size).
Just use a function instead of an operator. Your operator is pretty much identical to std::strcpy.
Note that the shown implementation is potentially unsafe (just like std::strcpy is) since it can easily accidentally overflow the target array.
use std::string
this will cause unpredictable results in a file handling.
std::string will not cause unpredicatble results in a file handling.

Related

Why std::string does not have (explicit) const char* cast

I like to know pro's and con's for having and not-having such cast. At several places including here on Stack Overflow I can see that the const char* cast is considered bad idea but I am not sure why?
Lack of the (const char*) and forcing to always use c_str() cast creates some problems when writing generic routines and templates.
void CheckStr(const char* s)
{
}
int main()
{
std::string s = "Hello World!";
// all below will not compile with
// Error: No suitable conversion function from "std::string" to "const char *" exists!
//CheckStr(s);
//CheckStr((const char*)s);
// strlen(s);
// the only way that works
CheckStr(s.c_str());
size_t n = strlen(s.c_str());
return 0;
}
For example, if I have a large number of text processing functions that accept const char* as input and I want to be able to use std::string each time I have to use c_str(). But in this way a template function can't be used for both std::string and const char* without additional efforts.
As a problem I can see some operator overloading issues but these are possible to solve.
For example, as [eerorika] pointed, with allowing implicit cast to pointer we are allowing involuntary the string class to be involved in boolean expressions. But we can easily solve this with deleting the bool operator. Even further, the cast operator can be forced to be explicit:
class String
{
public:
String() {}
String(const char* s) { m_str = s; }
const char* str() const { return m_str.c_str(); }
char* str() { return &m_str[0]; }
char operator[](int pos) const { return m_str[pos]; }
char& operator[](int pos) { return m_str[pos]; }
explicit operator const char*() const { return str(); } // cast operator
operator bool() const = delete;
protected:
std::string m_str;
};
int main()
{
String s = "Hello";
string s2 = "Hello";
if(s) // will not compile: it is a deleted function
{
cout << "Bool is allowed " << endl;
}
CheckStr((const char*)s);
return 0;
}
I like to know pro's and con's for having and not-having such cast.
Con: Implicit conversions often have behaviour that is surprising to the programmer.
For example, what would you expect from following program?
std::string some_string = "";
if (some_string)
std::cout << "true";
else
std::cout << "false";
Should the program be ill-formed because std::string is has no conversion to bool? Should the result depend on the content of the string? Would most programmers have the same expectation?
With the current std::string, the above would be ill-formed because there is no such conversion. This is good. Whatever the programmer expected, they'll find out their misunderstanding when they attempt to compile.
If std::string had a conversion to a pointer, then there would also be a conversion sequence to bool through the conversion to pointer. The above program would be well-formed. And the program would print true regardless of the content of the string, since c_str is never null. What if programmer instead expected that empty string would be false? What if they never intended either behaviour, but used a string there by accident?
What about the following program?
std::string some_string = "";
std::cout << some_string + 42;
Would you expect the program to be ill-formed because there is no such operator for string and int?
If there was implicit conversion to char*, the above would have undefined behaviour because it does pointer arithmetic and accesses the string buffer outside of its bounds.
// all below will not compile with
strlen(s);
This is actually a good thing. Most of the time, you don't want to call strlen(s). Usually, you should use s.size() because it is asymptotically faster. The need for strlen(s.c_str()) is so rare, that the little bit of verbosity is insignificant.
Forcing the use of .c_str() is great because it shows the reader of the program that it is not a std::string that is passed to the function / operator, but a char*. With implicit conversion, it is not possible to distinguish one from the other.
... creates some problems when writing generic routines and templates.
Such problems are not insurmountable.
If by "having a cast" you mean a user defined conversion operator, then the reason it does not have it is: to prevent you from using it implicitly, possibly inadvertently.
Historically, unpleasant consequences of an inadvertent use of such conversion stem the fact that in the original std::string (per C++98 specification) the operation was heavy and dangerous.
The original std::string was not trivially convertible to const char *, since the string object was not originally intended/required to store a null-terminator character. Under those circumstances, conversion to const char * was a potentially heavy operation that generally allocated an independent buffer and copied the entire controlled sequence to that buffer.
The independent buffer mentioned above (if used) had potentially "unexpected" lifetime. Any modifying operation on the original std::string object triggered invalidation/deallocation of that buffer, rendering previously returned pointers invalid.
It is never a good idea to implement such heavy and dangerous operations as implicitly-invokable conversion operators.
The original C++ standard (C++98) did not have such feature as explicit conversion operators. (They first appeared in C++11.) A dedicated named member function was the only way to somehow make the conversion explicit in C++98.
Today, in modern C++, we can define a conversion operator and still prevent it from being used implicitly (by using explicit keyword). One can argue that under such circumstances implementing the conversion by an operator is a reasonable approach. But I'd still argue that it is not a good idea. Even though the modern std::string is required to store its null-terminator (i.e. c_str() no longer produces an independent buffer), the pointer returned by the conversion to const char * is still "dangerous": many modification operations applied to std::string object may (and will) invalidate this pointer. To emphasize the fact that this is not a mere safe and innocent conversion, but rather an operation that produces a potentially dangerous pointer, it is quite reasonable to implement it by a named function.

usage of explicit keyword for constructors

I was trying to understand the usage of explicit keyword in c++ and looked at this question on SO
What does the explicit keyword mean in C++?
However, examples listed there (actually both top two answers) are not very clear regarding the usage. For example,
// classes example
#include <iostream>
using namespace std;
class String {
public:
explicit String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
String::String(int n)
{
cout<<"Entered int section";
}
String::String(const char *p)
{
cout<<"Entered char section";
}
int main () {
String mystring('x');
return 0;
}
Now I have declared String constructor as explicit, but lets say if I dont list it as explicit, if I call the constructor as,
String mystring('x');
OR
String mystring = 'x';
In both cases, I will enter the int section. Until and unless I specify the type of value, it defaults to int. Even if i get more specific with arguments, like declare one as int, other as double, and not use explicit with constructor name and call it this way
String mystring(2.5);
Or this way
String mystring = 2.5;
It will always default to the constructor with double as argument. So, I am having hard time understanding the real usage of explicit. Can you provide me an example where not using explicit will be a real failure?
explicit is intended to prevent implicit conversions. Anytime you use something like String(foo);, that's an explicit conversion, so using explicit won't change whether it succeeds or fails.
Therefore, let's look at a scenario that does involve implicit conversion. Let's start with your String class:
class String {
public:
explicit String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
Then let's define a function that receives a parameter of type String (could also be String const &, but String will do for the moment):
int f(String);
Your constructors allow implicit conversion from char const *, but only explicit conversion from int. This means if I call:
f("this is a string");
...the compiler will generate the code to construct a String object from the string literal, and then call f with that String object.
If, however, you attempt to call:
f(2);
It will fail, because the String constructor that takes an int parameter has been marked explicit. That means if I want to convert an int to a String, I have to do it explicitly:
f(String(2));
If the String(char const *); constructor were also marked explicit, then you wouldn't be able to call f("this is a string") either--you'd have to use f(String("this is a string"));
Note, however, that explicit only controls implicit conversion from some type foo to the type you defined. It has no effect on implicit conversion from some other type to the type your explicit constructor takes. So, your explicit constructor that takes type int will still take a floating point parameter:
f(String(1.2))
...because that involves an implicit conversion from double to int followed by an explicit conversion from int to String. If you want to prohibit a conversion from double to String, you'd do it by (for example) providing an overloaded constructor that takes a double, but then throws:
String(double) { throw("Conversion from double not allowed"); }
Now the implicit conversion from double to int won't happen--the double will be passed directly to your ctor without conversion.
As to what using explicit accomplishes: the primary point of using explicit is to prevent code from compiling that would otherwise compile. When combined with overloading, implicit conversions can lead to some rather strange selections at times.
It's easier to demonstrate a problem with conversion operators rather than constructors (because you can do it with only one class). For example, let's consider a tiny string class fairly similar to a lot that were written before people understood as much about how problematic implicit conversions can be:
class Foo {
std::string data;
public:
Foo(char const *s) : data(s) { }
Foo operator+(Foo const &other) { return (data + other.data).c_str(); }
operator char const *() { return data.c_str(); }
};
(I've cheated by using std::string to store the data, but the same would be true if I did like they did and stored a char *, and used new to allocate the memory).
Now, this makes things like this work fine:
Foo a("a");
Foo b("b");
std::cout << a + b;
...and, (of course) the result is that it prints out ab. But what happens if the user makes a minor mistake and types - where they intended to type +?
That's where things get ugly--the code still compiles and "works" (for some definition of the word), but prints out nonsense. In a quick test on my machine, I got -24, but don't count on duplicating that particular result.
The problem here stems from allowing implicit conversion from String to char *. When we try to subtract the two String objects, the compiler tries to figure out what we meant. Since it can't subtract them directly, it looks at whether it can convert them to some type that supports subtraction--and sure enough, char const * supports subtraction, so it converts both our String objects to char const *, then subtracts the two pointers.
If we mark that conversion as explicit instead:
explicit operator char const *() { return data.c_str(); }
...the code that tries to subtract two String objects simply won't compile.
The same basic idea can/does apply to explicit constructors, but the code to demonstrate it gets longer because we generally need at least a couple of different classes involved.

Calling the parenthesis overload given a pointer

I can overload the parenthesis operator using the following signature:
char& operator()(const int r, const int c);
The intended usage of this would be:
// myObj is an object of type MyClass
myObj(2,3) = 'X'
char Y = myObj(2,3);
Which works as I expect. However, using the parenthesis operator when dealing with a pointer becomes convoluted. I would like to do:
// pMyObj is a pointer to an object of type MyClass
pMyObj->(2,3) = 'X';
char Y = pMyObj->(2,3);
However, such syntax yields the error Error: expected a member name (in VisualStudio at least).
The following does work but seems convoluted to me with a dereference and more parentheses than arguments.
char X = (*pMyObj)(2,3);
Is there a way to use the -> operator to call the () overload?
Yes there is, but you won't like it:
pMyObj->operator()(2,3);
You could also create a reference to the pointed to object and do
MyObj& rMyObj = *pMyObj;
char Y = rMyObj(2, 3);
which might be a good alternative if your code will be read by people who could be confused by
pMyObj->operator()(2,3);
if you do as below
#define SUB operator()
Then you can write things like this ...
pMyObj->SUB(2,3)
not as elegant as Fortran ;-) but perhaps not too ugly for actual use

Overloading = operator in c++ in case of strings

I want to perform the following
int p = "xyz"
I should contain 3, i.e, the length of the string on the right side of =.
I tried making a class String with a char * data member but i was having trouble overloading the = operator since we have to pass atleast one object of the class we are overloading the operator for.
please help.
I tried to do the following:
friend void operator=(int, char*)
but it won't work since there is no argument of the class type.
It's impossible to do this, operator= is not overloadable for integer type.
See this answer.
If you use std::string, the solution is simple:
std::string p = "abc";
int l = p.size(); // Better than l = p, because self describing

How do I implicitly specialise conversion?

With the following code, if I attempt to convert a template array to std::string, instead of the compiler using the expected std::string conversion method, it raises an ambiguity resolution problem (as it attempts to call the array conversion methods):
#include <iostream>
template<typename TemplateItem>
class TestA
{
public:
TemplateItem Array[10];
operator const TemplateItem *() const {return Array;}
operator const std::string() const;
};
template<>
TestA<char>::operator const std::string() const
{
std::string Temp("");
return Temp;
}
int main()
{
TestA<char> Test2;
std::string Temp("Empty");
Temp = Test2; //Ambiguity error. std::string or TemplateItem * ?
return 0;
}
What modification do I need to make to the code in order to make it so the code correctly and implicitly resolve to the std::string conversion function? Especially given the const TemplateItem * would be treated as a null-terminated array (which it won't likely be).
First, the reason you have ambiguity: you provide both conversion to char* and conversion to std::string const, and std::string likes them both.
By the way, before getting to your question, the const in operator std::string const was once a good idea, advocated by e.g. Scott Meyers, but is nowadays ungood: it prevents efficient moving.
Anyway, re the question, just avoid implicit conversions. Make those conversions explicit. Now I answered that in response to another SO question, and someone (I believe the person was trolling) commented that C++98 doesn't support explicit type conversion operators. Which was true enough, technically, but pretty stupid as a technical comment. Because you don't need to use the explicit keyword (supported by C++11), and indeed that's not a good way to make the conversions explicit. Instead just name those conversions: use named member functions, not conversion operators.
#include <iostream>
template<typename TemplateItem>
class TestA
{
public:
TemplateItem Array[10];
TemplateItem const* data() const { return Array; }
std::string str() const;
};
template<>
std::string TestA<char>::str() const
{
return "";
}
int main()
{
TestA<char> test2;
std::string temp( "Empty" );
temp = test2.str(); // OK.
temp = test2.data(); // Also OK.
}
Cheers & hth.
I will add, after thinking about it, here's the reasoning and what should be done:
The operator const TemplateItem *(); Should be deleted.
Why? There never will be an instance where you would want implicit conversion of TestA (or any template class) to a TemplateItem array with an unknown size - because this has absolutely no bounds checking (array could be sizeof 1 or 10 or 1000 or 0) and would likely cause a crash if a calling function or class received the array with no idea what size it is.
If the array is needed, either use the operator[] for direct items, or GetArray (which would signal the user intends to pass an unknown-length array).
Retain operator const std::string. Code will compile. Possible memory issues down the line averted.
(Alf for his efforts has been selected as the 'correct' answer, although this is the more logical option)