Shouldn't char* implicitly converted to std::string? - c++

Here is my code:
#include <iostream>
using namespace std;
struct ST {};
bool operator==(const struct ST *s1, const string &s2) {
return true;
}
int main() {
struct ST *st = new ST();
const char *p = "abc";
if (st == p) {
return 0;
}
return 1;
}
I get compile error:
prog.cpp:14:12: error: comparison between distinct pointer types ‘ST*’ and ‘const char*’ lacks a cast [-fpermissive]
if (st == p) {
^
I wonder why the implicit conversion from char* to string does not work here?
UPDATE
Anton's answer makes sense, I updated the code:
#include <string>
using namespace std;
struct ST {};
bool operator==(const struct ST s1, const string &s2) {
return true;
}
int main() {
struct ST st;
const char *p = "abc";
if (st == p) {
return 0;
}
return 1;
}
Now it compiles.

§13.3.1.2 Operators in expressions [over.match.oper] states:
If no operand of an operator in an expression has a type that is a class or an enumeration, the operator is assumed to be a built-in operator and interpreted according to Clause 5.
This is exactly your case: operator=='s arguments are pointers, so it's considered to be built-in and compiler doesn't look for possible overloads.

Absolutely not.
First of all, you are trying to use a pointer in place of a reference. Any similarity between a pointer and a reference is a implementation detail. Remember the motto: "Implementation Is Irrelevant!"
Next, and more directly to your question, a std::string and a char* are quite different, even if they are used to represent the same things. Conversion between them was deliberately made difficult to prevent using them interchangably.

Related

Possible to overload operator* to multiple an int and a char*?

I would like to get functionality so I can do this:
std::cout << "here's a message" << 5*"\n";
I tried the following:
std::string operator* (int lhs, const char* rhs) {
std::string r = "";
for(int i = 0; i < lhs; i++) {
r += rhs;
}
return r;
}
And I got this error message:
error: ‘std::string operator*(int, const char*)’ must have an argument of class or enumerated type
According to the answers in this SO post What does 'must have an argument of class or enumerated type' actually mean it almost seems like I can't do this period. Is that really the case? If not, how do I fix this or arrange a workaround?
What I know I can do is have rhs as a std::string, but then the whole point of the exercise is half foregone, as 5*std::string("\n") is quite clunky.
From [over.oper]:
An operator function shall either be a non-static member function or be a non-member function that has
at least one parameter whose type is a class, a reference to a class, an enumeration, or a reference to an
enumeration.
So you can't overload an operator whose parameters are both builtins. Furthermore, in order for operator*(int, std::string) to be found, it'd have to be in namespace std and it's ill-formed to add definitions to that namespace.
Instead, you could simply provide a small wrapper:
struct Mult { int value; };
and provide overloads for it:
std::string operator*(const Mult&, const char* );
std::string operator*(const char*, const Mult& );
From C++ FAQ here,
C++ language requires that your operator overloads take at least one
operand of a “class type” or enumeration type. The C++ language will
not let you define an operator all of whose operands / parameters are
of primitive types.
You should be able to achive it with user-defined literals. For instance:
#include <iostream>
#include <string>
std::string operator"" _s(const char* s) { return std::string(s); }
std::string operator"" _s(const char* s, std::size_t len) { return std::string(s, len); }
std::string operator* (unsigned int k, std::string s) {
std::string t;
for (unsigned int i = 0; i < k; ++i)
t += s;
return t;
}
std::string operator* (std::string s, unsigned int k) { return k * s; }
int main() {
std::cout << "Jump!"_s * 5 << "\n";
}
You neither can nor must overload that op;
string ctor (2) does the job for you
#include <iostream>
#include <string>
int main() {
std::cout << "here's a message:\n"
<< std::string(5, '\n')
<< "EOF" << std::endl;
}
output:
here's a message:
EOF
(live at Coliru)

Why does this assignment operation results in ambiguous function call?

Consider following program it compiles and runs fine:
#include <iostream>
#include <string>
using std::string;
struct BB
{
// generic cast
template<typename T>
operator T() const
{
std::cout<<"Generic cast\n";
return 0;
}
// string cast
operator string() const
{
std::cout<<"string cast\n";
return string("hello");
}
};
int main()
{
BB b;
string s = b; // copy constructor
}
But If I slightly change the main() function's code in like following:
int main()
{
BB b;
string s;
s = b;
}
Compiler give following error message (See live demo here)
[Error] ambiguous overload for 'operator=' (operand types are 'std::string {aka std::basic_string<char>}' and 'BB')
Why this call is ambiguous? What is the reason behind that? It looks like there are so many overloaded operator= like one for char, one for char*, one for const char* etc. That's the above program puts compiler into ambiguity.
Your problem is your template conversion operator.
template<typename T>
operator T() const
{
std::cout << "Generic cast\n";
return 0;
}
Allows BB to be converted to anything. Because of that all of the overloads of std::string::operator= that take a different type can be considered. Since they are all valid there is no way to resolve the ambiguity and you get the error.
If you removed the template conversion then it will compile. The template conversion could also be marked as explicit and then you can use a static_cast to the type you want.
You call operator =, but it would be the same if it were a regular function:
void f(int x);
void f(const string &y);
int main() {
BB b;
f(b);
return 0;
}
Since BB can be cast in either int or string, the compiler has no idea which f function to call.
The only reason why your first example works is because the copy constructor is called there, and it only takes const string& as an argument, so there's no multiple choices.

Strange compile error when overloading operator !=

I have created my own version of operator != so that I can use p != NULL instead of p->b == 0. However, when compiling it (g++ v4.7.3 on Ubuntu 12.04), I get the error:
$ g++ -std=c++11 te2b.cc
te2b.cc:8:41: error: ‘bool operator!=(strDum*, void*)’ must have an argument of class or enumerated type
Here is the code snippet:
#include <vector>
#include <iostream>
struct strDum {
int a;
int b;
};
bool operator!= (strDum *p, void *unused) {
return p->b != 0;
}
int main(void) {
strDum *x = (strDum*) malloc(sizeof(strDum) * 4);
x[0].a = 10; x[0].b = 20;
x[1].a = 100; x[1].b = 200;
x[2].a = 1000; x[2].b = 0;
strDum *y;
for (y=x; y!= NULL; y++) {
printf("%5d %5d\n", y->a, y->b);
}
return 0;
}
Any ideas?
By the way, I prefer p != NULL to p->b == 0 because the structure strDum and the criteria may change frequently (p != NULL may become p->c == 0)
UPDATE1
As the declaration bool operator!= (strDum *p, void *unused) shows, p is only going to be compared with "NULL".
You can not declare an operator which only takes pointers as arguments. From the standard:
13.5.6 Overloaded operators [over.oper]
An operator function shall either be a non-static member function or
be a non-member function that has at least one parameter whose type is
a class, a reference to a class, an enumeration, or a reference to an
enumeration. It is not possible to change the precedence, grouping, or
number of operands of operators. The meaning of the operators
= , (unary) & , and , (comma), predefined for each type, can be changed for specific class and enumeration types by defining operator
functions that implement these operators. Operator functions are
inherited in the same manner as other base class functions.
You can't declare an operator overloaded function that takes pointers as arguments.
Consider doing this instead:
#include <iostream>
struct strDum {
int a;
int b;
};
template <typename T>
bool operator!= (strDum const& p, T const& number) {
return p.b != number;
}
//may want to include other operators like == as well
int main(void) {
strDum *test = new strDum;
if (*test != 0){
// data member did not equal zero ...
}
return 0;
}

Storing std::to_string(x).c_str() in member variable produces garbage

I have this very simple C++ program:
using namespace std;
class TheClass
{
private:
const char *_numberString;
public:
TheClass(int number)
{
_numberString = to_string(number).c_str();
}
operator const char *()
{
return _numberString;
}
};
int main(int argc, const char * argv[])
{
TheClass instance = 123;
cout << (const char *)instance << endl;
return 0;
}
When I run it in Xcode, it logs \367\277_\377. If I change it to this however:
using namespace std;
class TheClass
{
public: // Change 1/2
const char *_numberString;
public:
TheClass(int number)
{
_numberString = to_string(number).c_str();
}
operator const char *()
{
return _numberString;
}
};
int main(int argc, const char * argv[])
{
TheClass instance = 123;
instance._numberString = to_string(123).c_str(); // Change 2/2
cout << (const char *)instance << endl;
return 0;
}
it logs 123 like it should. I can't see what I'm doing wrong. Even if I change 123 to another number the exact same thing is logged.
At this point
_numberString = to_string(number).c_str();
you are storing a pointer to the interned data of a temporary std::string value, that is invalidated after that line of code.
Accessing _numberString effectively calls undefined behavior.
As mentioned in comments, there's no point to keep the _numberString1 member as const char*. Use a std::string member instead:
class TheClass {
private:
std::string numberString_;
public:
TheClass(int number) : numberString_(to_string(number)) {
}
operator const std::string& () {
return numberString_;
}
};
1) You shouldn't use prefixed _ for class member names, that's reserved for compiler and standard implementation intrinsics. If you dislike patterns like m_ or other prefix conventions (like me), just use a postfix _ as shown in my sample.
The return value of c_str is only valid for as long as the string is in scope (and unaltered). Your anonymous temporary goes out of scope at the end of the statement.
Consider having a std::string as a member variable rather than a pointer type, or store the numeric value itself.
c_str() returns a pointer to the buffer of the std::string instance it's call on. The object returned by std::to_string() is a temporary and is destroyed at the end of the constructor body. That leaves _numberString pointing to an object that has since been destroyed.
The second piece of code doesn't have to work. You have the same problem as in the first one. The fact that it works is an effect of undefined behavior.

Operator overloading c++ (<<)

This the below program i have written for some test.
class tgsetmap
{
public:
std::map<std::string,std::string> tgsetlist;
void operator<<(const char *str1,const char *str2)
{
tgsetlist.insert( std::map<std::string,std::string>::value_type(str1,str2));
}
};
int main()
{
tgsetmap obj;
obj<<("tgset10","mystring");
obj.tgsetlist.size();
}
This throws a compilation error:
"test.cc", line 10: Error: Illegal number of arguments for tgsetmap::operator<<(const char, const char*).
"test.cc", line 22: Error: The operation "tgsetmap << const char*" is illegal.
2 Error(s) detected.*
Am i wrong some where?
You can't force operator<< to take two arguments on right-hand side. The following code:
obj<<("tgset10","mystring");
does not work as a function call with two arguments but instead just uses the , operator. But it's probably not what you are interested in.
If you need to pass two arguments to the << operator, you need to wrap them in some other (single) type. For example, you could use the standard std::pair, i.e. std::pair<const char*, const char*>.
But note that the operator<< should also return some reasonable type suitable for << chaining. That would probably be a tgsetmap& in your case. The following version should work fine:
#include <map>
#include <string>
#include <iostream>
class tgsetmap
{
public:
typedef std::map<std::string, std::string> list_type;
typedef list_type::value_type item_type;
list_type tgsetlist;
tgsetmap& operator<<(item_type item)
{
tgsetlist.insert(item);
return *this;
}
};
int main()
{
tgsetmap obj;
obj << tgsetmap::item_type("tgset10","mystring")
<< tgsetmap::item_type("tgset20","anotherstring");
std::cout << obj.tgsetlist.size() << std::endl;
}
Note that I've added typedefs to not have to repeat the type names over and over again. I've also made operator<< return a tgsetmap& so that << could be chained (used like in the modified main() above). And finally, I've reused the std::map<...>::value_type to make it simpler but you could also use any other type of your own.
But I believe that you may prefer using a regular method instead. Something like:
void add(const char *str1, const char *str2)
{
tgsetlist.insert( std::map<std::string, std::string>::value_type(str1, str2));
}
(inside the class declaration), and then:
obj.add("tgset10", "mystring");
The operator<< inside of a class must be overloaded like this:
T T::operator <<(const T& b) const;
If you want to overload it with 2 arguments, you can do it outside of a class:
T operator <<(const T& a, const T& b);
My compiler, for example, gives a more detailed error message for the code you posted:
If you are not sure about an operator overloading syntax, there is a wiki article about it.
Yes. operator << is binary operator. not ternary. not forget about this pointer.
As mentioned, the << is binary operator, so there is no way it can take more than two args(One should be this if you are declaring inside the class or a LHS if you are declaring outside the class). However you can accomplish the same functionality by doing obj<<"tgset10". <<"mystring";. But since << is a binary operator, you have to do some hack for this.
For this, I ve assigned a static variable op_count, where in I will determine if it is the value or the type. And another static variable temp_str to store the previous value across invocations.
class tgsetmap
{
public:
std::map<std::string,std::string> tgsetlist;
static int op_count = 0;
static const char *temp_str;
tgsetmap& operator<<(const char *str)
{
op_count++;
if (op_count%2 != 0) {
temp_str = str;
}
else {
tgsetlist.insert( std::map<std::string,std::string>::value_type(temp_str,str));
}
return this;
}
};
So you can do
int main()
{
tgsetmap obj;
obj<<"tgset10"<<"mystring";
obj.tgsetlist.size();
}
Or simply you can embed the value and type in the same string using some separator,
value:type = separator is :
value_type = separator is _.