Unexpected problems concatenating strings - c++

I'm trying to concatenate strings using +, but there is some weird stuff going on. Here is my "Grade" class I have for a class project:
#pragma once
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Grade {
private:
string className;
string student;
string letter;
public:
Grade(string c, string s, string l) : className(c), student(s), letter(l) {}
string getLetterGrade() const { return letter; }
string getClassName() const { return className; }
string getStudent() const { return student; }
void setLetterGrade(string l) { letter = l; return;}
void setClassName(string c) { className = c; return;}
void setStudnet(string s) { student = s; return;}
string toString() const { string output = "hello"+student; return output; }
};
Obviously, the toString() method isn't currently what I want it to be.
If I run the toString() as above, I get "hello529173860" out, as expected. However, if I change the line to:
string toString() const { string output = student+"hello"; return output; }
then the output is "hello3860". This isn't just putting the hello string on the front, but its replacing characters from the student string in the process... somehow?
Furthermore, if I try to use:
string toString() const { string output = "hello"+" world"; return output; }
I get an error:
Grade.h: In member function ‘std::string Grade::toString() const’:
Grade.h:29:53: error: invalid operands of types ‘const char [6]’ and ‘const char [7]’ to binary ‘operator+’
string toString() const { string output = "hello"+" world"; return output; }
^
I'm really at a loss for what it going on here... especially since I have done string concatenation earlier in the program without issue.
What I would like is to output something like:
"student+[some white space]+letter+[some white space]+className"

A std::string can be added to anything (another std::string, double-quoted string literal, char) and provide intuitive results, but if you try to add a double-quoted string literal to another string literal or a char then it won't "work":
a string literal added to a char or other integral value will undergo Standard Conversion to a const char*, then any number added to the pointer will move along the literal that number of characters: if the offset isn't inside the string literal then you get undefined behaviour if you dereference (use) the resultant pointer,
two string literals just can't be added, even after decaying to two pointers, so you'll get a compile-time error.
Sometimes you will want to explicitly construct a std::string so that concatenation with other values works as you'd like: e.g. my_string = std::string("hello ") + my_const_char_ptr + '\n'.
Examples:
std::string s = "Hello";
s + " World"; // ok
"Hello " + s; // ok
"Hello " + "World"; // NOT ok - two string literals
s += " World"; // ok
s += " Goodbye " + "World"; // NOT ok - "+" evaluated before "+="
s += std::string(" Goodbye ") + "World"; // OK - right-hand-side no longer two literals
// BUT may be slower than two "s +="

The constant strings "hello" and "world" are nothing but compile time constants of type const char*. Just like you cannot add two int pointers:
int *p1, *p2;
p1+p1; // Error
You cannot add two (const) char* objects. This is against the C/C++ language rules. If you must concatenate two const-char-pointers, you can just place them together:
"hello" "world"
This technique is mainly useful if you use them along with macros. For example:
// Over simplified
#define ONE(_x) _x
#define TWO(_x) _X
ONE("This is") TWO(" concatenation")
If you are adding two (or more) runtime C-Strings, you must use strXXX functions (like strcat), or better use std::string.

There is no operator + for character arrays. So it is obvious that this code
string toString() const { string output = "hello"+" world"; return output; }
is invalid.
In this expression "hello"+" world" there are used two string literals that have the types correspondingly const char[6] and const char[7]. The operator + is not defined for arrays.
You could just write
string toString() const { return string( "hello" ) + " world"; }
In this case there is used the operator + overloaded for the class std::string. Declaration of the local variable input is redundant. So you could simplify the function even the following way
string toString() const { return "hello world"; }

Related

Type id of std::string for variable vs. string in argument?

I referred to http://en.cppreference.com/w/cpp/language/typeid to write code which does different things for different types.
The code is as below and the explanation is given in the comments.
#include <iostream>
#include <typeinfo>
using namespace std;
template <typename T>
void test_template(const T &t)
{
if (typeid(t) == typeid(double))
cout <<"double\n";
if (typeid(t) == typeid(string))
cout <<"string\n";
if (typeid(t) == typeid(int))
cout <<"int\n";
}
int main()
{
auto a = -1;
string str = "ok";
test_template(a); // Prints int
test_template("Helloworld"); // Does not print string
test_template(str); // Prints string
test_template(10.00); // Prints double
return 0;
}
Why does test_template(str) print "string" whereas test_template("Helloworld") does not?
BTW, my g++ version is g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609.
In this call
test_template("Helloworld"); // Does not print string
the argument "Helloworld" is a string literal that has type const char[11].
Because the function parameter is a referenced type
void test_template(const T &t)
^^^
then within the function the argument (more precisely the parameter) has the type const char ( &t )[11].
String literals in C++ have types of constant character arrays with the number of elements equal to the number of characters in string literal plus the terminating zero.
In this call
test_template(str);
the argument has type std::string because the variable str is declared like
string str = "ok";
^^^^^^
It was initialized by the string literal "ok" nevertheless the object itself is of the type std::string.
String literals in C++ are of type const char[N+1], where N is the number of characters in the string. std::string is a standard library class which owns a string and provides a number of operations over it. A std::string can be constructed from a const char[N], but they are not the same thing.
String literals like "Helloworld" are constants arrays of characters.
The std::string class have a constructor that can take pointers to string literals, but a string literal is in itself not a std::string object.
As a side-note, using a function like your is considered a code-smell and bad design. Use overloaded functions taking different arguments instead. That will also solve your problem with the strings.

Automatically Concatenate Strings and Int C++

In Lua (apologise, I like working with it the best), the conversion between int and string is done automatically, so
"hi"..2
would result as
"hi2"
In C++ (cause I can't seem to get the default C++11 stoi() and to_string() methods to work) I defined these for myself:
int stoi(string str) {
char* ptr;
strtol(str.c_str(), &ptr, 10);
}
string to_string(int i) {
char* buf;
sprintf(buf, "%d", i);
return buf;
}
which are basically how the default ones are defined anyways.
Then I did this:
string operator+ (string& stuff, int why) {
stuff.append(to_string(why));
}
I tried it on the following code:
void print(string str) {
cout << str << endl;
}
int main() {
cout << stoi("1") + 2 << endl;
print("die" + 1);
return 0;
}
And it outputs
3
ie
Why is this so, and how can I fix it?
EDIT:
Here's what the code looks like now:
using namespace std;
string to_string(int i) {
char* buf;
sprintf(buf, "%d", i);
return buf;
}
string operator+ (string stuff, int why) {
stuff.append(to_string(why));
return stuff;
}
int main() {
cout << string("die") + 2 << endl;
return 0;
}
And it just keeps giving me stackdumps.
Replace print("die" + 1); with cout << std::string("die") + 1;
print() doesn't know what to do with strings. Use std::cout. "die" is a char*, +1 will increment the pointer.
std::string to_string(int i) {
char buf[(sizeof(int)*CHAR_BIT+2)/3+3];
sprintf(buf, "%d", i);
return buf;
}
You need to make an actual buffer to print to. The math is a quick over-estimate of big the largest decimal int is in characters; 3 bits can fit in 1 decimal character, plus null, plus negation, plus rounding, plus 1 for good measure. Hopefully I did not err: do some testing.
Also use snprintf instead of sprintf while you are at it: buffer overflows are not to be toyed with.
The next problem is that "hello" is not a std::string, It is a char const[6] -- an array of 6 char. It can be converted tomstd::string, but +1 will instead convert it to a pointer to the first character, then +1 it to the 2nd character.
Cast it to std::string before.
Finally, it is ambiguous in the standard (really) of pverloading an operator on std::string + int is legal. It is definitely poor practice, as you cannot do it in std legally, and you should overload operators in the type's namespace (so ADL works): these two conflict. On top of that, if std in the future adds such a + your code starts behaving strangely. On top of that, operators are part of a class's interface, and modifying the interface of a class you do not 'own' is rude and a bad habit.
Write your own string class that owns a std::string rather. Or a string view.
Finally, consider telling your compiler to use c++11, you probably just need to pass a flag to it like -std=c++11.
std::string s1("h1");
std::string s2("2");
s1 += s2;
If you are using C++11 compatible compiler you can convert int to string like this:
int i = 2;
std::string s = std::to_string(i);
If you are using Boost library:
#include <boost/lexical_cast.hpp>
int i = 2;
std::string s = boost::lexical_cast<std::string>(i);
Please do not use raw char pointers in C++ for strings.
overloading the operator+ on other than your own types it at best dangerous.
Just use std::to_string in conjunction with operator+ or +=, e.g.
std::string x = "hi";
x += std::to_string(2);
C++14 introduces a user-defined literal that takes a string literal (conversions are applied to make this a pointer) and returns a std::string. In C++11, you can just write your own (this is taken from libstdc++):
inline std::string
operator""_s(const char* str, size_t len)
{
return std::string{str, len};
}
(Note: UDLs without a preceding underscore are reserved names)
And you can use it like this:
// Assumes operator+ is overloaded
print("die"_s + 1);
Demo
"die" is not a std::string. It's a string literal.
Thus when you add 1 to the string literal, it decays to a const char* and the + 1 simply increments that pointer — to next char, 'i'.
Then you call print with the incremented pointer, which causes a std::string to be constructed using that pointer. Since it pointed to the 'i' character, to constructed string is initialized to "ie".
You must first make a std::string out of your string literal to make it call your operator+:
std::cout << std::string("die") + 1;
And then make a few fixes to your operator+:
string operator+ (string stuff, int why) {
return stuff.append(to_string(why));
}
Now it works.

C++ const std::string assignment error if the right handside contains concatenation on string literals

I am new to C++, I encountered this oddity with const std::string assignment
This works fine:
const std::string hello = "Hello";
const std::string message = hello + " world";
This gives compiler error:
const std::string message = "Hello" + " world";
I do not understand why this is, anyone ?
Thanks
There is no operator + defined that takes two pointers of type const char* and returns a new array of characters containing the concatenation of the strings they point to.
What you can do is:
std::string message = std::string("Hello") + "world";
Or even:
std::string message = "Hello" + std::string("world");
To concatenate literal strings, you don't need to put extra + between them, just put them together without any operator will perform the concatenation:
std::string message = "Hello" "world";
printf("%s\n", message.c_str());
and the above code will give you:
Helloworld

How to copy a string of std::string type in C++?

I used the strcpy() function and it only works if I use C-string arrays like:
char a[6] = "text";
char b[6] = "image";
strcpy(a,b);
but whenever I use
string a = "text";
string b = "image";
strcpy(a,b);
I get this error:
functions.cpp: no matching function for call to strcpy(std::string&, std::string&)
How to copy 2 strings of string data type in C++?
You shouldn't use strcpy() to copy a std::string, only use it for C-Style strings.
If you want to copy a to b then just use the = operator.
string a = "text";
string b = "image";
b = a;
strcpy is only for C strings. For std::string you copy it like any C++ object.
std::string a = "text";
std::string b = a; // copy a into b
If you want to concatenate strings you can use the + operator:
std::string a = "text";
std::string b = "image";
a = a + b; // or a += b;
You can even do many at once:
std::string c = a + " " + b + "hello";
Although "hello" + " world" doesn't work as you might expect. You need an explicit std::string to be in there: std::string("Hello") + "world"
strcpy example:
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[]="Sample string" ;
char str2[40] ;
strcpy (str2,str1) ;
printf ("str1: %s\n",str1) ;
return 0 ;
}
Output: str1: Sample string
Your case:
A simple = operator should do the job.
string str1="Sample string" ;
string str2 = str1 ;
Caesar's solution is the best in my opinion, but if you still insist to use the strcpy function, then after you have your strings ready:
string a = "text";
string b = "image";
You can try either:
strcpy(a.data(), b.data());
or
strcpy(a.c_str(), b.c_str());
Just call either the data() or c_str() member functions of the std::string class, to get the char* pointer of the string object.
The strcpy() function doesn't have overload to accept two std::string objects as parameters.
It has only one overload to accept two char* pointers as parameters.
Both data and c_str return what does strcpy() want exactly.

Why is part of my code not executed?

I'm using Visual C++ to compile my plug-in for Cinema 4D.
GeDebugOut("-->");
subroot = NULL;
head = NULL;
tail = NULL;
success = PolygonizeHierarchy(source, hh, head, tail, &subroot, malloc);
if (!success) {
/* .. */
}
String str("not set.");
if (subroot) {
GeDebugOut("yes");
str = "yes!";
GeDebugOut("Subroot name: " + subroot->GetName());
}
else {
GeDebugOut("no");
str = "no!";
}
GeDebugOut("Is there a subroot? " + str);
GeDebugOut("<--");
The expected output is the following:
-->
yes
Subroot name: Cube
Is there a subroot? yes
<--
(or the same with "no" instead.) But I get
-->
yes
<--
Why are two prints missing here?
This is the declaration of GeDebugOut.
void GeDebugOut(const CHAR* s, ...);
void GeDebugOut(const String& s);
The String class is concatenateable. It overloads the + operator.
String(void);
String(const String& cs);
String(const UWORD* s);
String(const CHAR* cstr, STRINGENCODING type = STRINGENCODING_XBIT);
String(LONG count, UWORD fillch);
friend const String operator +(const String& Str1, const String& Str2);
const String& operator +=(const String& Str);
You need to use GeDebugOut like you use printf:
GeDebugOut("Some message = %s ", whatever);
where whatever is a c-string, i.e its type is char*.
Since an overload of GeDebugOut accepts String type also, then I think you need to use unicode as:
GeDebugOut(L"Is there a subroot? " + str);
// ^ note this!
because my suspicion is that if unicode is enabled, then CHAR is basically wchar_t, not char. And because of this, the string concatenation doesn't work, as the string-literal doesn't implicitly get converted into String type, to be passed to + overload.
You cannot append a string to a string literal.
"Is there a subroot" is a string literal and the compiler will see the use of it as a pointer to that literal.
A better way would be to do:
GeDebugOut("Is there a subroot? %s ", str);
As you mentioned, there are two versions of GeDebugOut the compiler can choose from:
void GeDebugOut(const CHAR* s, ...);
void GeDebugOut(const String& s);
when it encounters:
GeDebugOut("Is there a subroot? " + str);
"Is there a subroot" is a string literal, which translates to type const char*. I suspect String has a conversion operator to some numeric type. So the compiler is choosing the first overload.
This is resulting in behavior you're not expecting, because the + operation for const char* is pointer arithmetic, not string concatenation, so you're calling GeDebugOut on the pointer sum of your string literal, and whatever the output of that const char* conversion of str is.
There's several ways you can correct this. As another mentioned, you can change it to printf-like syntax. Or you can force it to use the String overlaod like so:
GeDebugOut(String("Is there a subroot?") + str);