Why does "auto" declare strings as const char* instead of std::string? - c++

I made a template which adds the data it is given. If I use it like this, the compiler declares in_1 and in_2 as const char *, and the code doesn't compile.
#include <iostream>
using namespace std;
template <class T>
T addstuff(T part_1, T part_2){
return(part_1+part_2);
}
int main(int argc, char const *argv[])
{
auto in_1="Shut ";
auto in_2="up.";
cout<<addstuff(in_1, in_2)<<endl;
return 0;
}
If I declare in_1 and in_2 std::string, it works like a charm.
Why can't (or doesn't) the compiler declare those strings automatically std::string?

The reason you can't "write" to your auto variable is that it's a const char * or const char [1], because that is the type of any string constant.
The point of auto is to resolve to the simplest possible type which "works" for the type of the assignment. The compiler does not "look forward to see what you are doing with the variable", so it doesn't understand that later on you will want to write into this variable, and use it to store a string, so std::string would make more sense.
You code could be made to work in many different ways, here's one that makes some sense:
std::string default_name = "";
auto name = default_name;
cin >> name;

If you use string literals, auto will work as expected.
In C++14, C++17 or C++20, you can place an s after the quotes, and it will create a std::string instead of a const char* string.
This can be used together with auto to create a std::string:
auto hello = "hello"s;
String literals are not enabled by default. One way of enabling string literals is to place the following at the top of the source file:
#include <string>
using namespace std::string_literals;
As an example, this loop works for std::string (with s added to the string literal), but not for const char* type string literals:
for (auto &x : hello) {
std::cout << "letter: " << x << std::endl;
}
Here is the cppreference page for the ""s operator.

Because string literals have type const char[N+1], not std::string.
This is just a fact of the language.
They could have made it so that auto has a special case for string literals, but that would be inconsistent, surprising and of very little benefit.

auto will declare the variable as the compile-time type of the expression you initialize it to.
String literals are of type const char*, not std::string.

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.

const char * to char * in my case

Although I know that converting const char * to char * is almost banned in C/C++ because of many problems, I am caught up in a situation where I think I have to convert const char * to char *.
I get a string from a text file as string by using c_str and I want to modify this string. However the problem is that c_str converts string into const char *. So is it a good choice to use strdup in this case or is there any better idea in doing this?
Actually, you can modify the std::string object directly, as illustrated in the little program below:
int main()
{
std::string s("Hello World");
for(char& c : s)
{
c = toupper(c);
}
s[0] = 'h';
s[6] = 'w';
s.resize(12);
s[11] = '!';
std::cout << s;
return 0;
}
I think, it is self-explaining. Although you mentioned that size does not change, I added an example for that case, too, see resize. There are yet other ways to manipulate the string, such as insert. Have a look at the std::string documentation.
Why is using &s[0] better than c_str?
std::basic_string class has data() and c_str(). However, as you know, these function's result type is const CharType*(CharType: char, wchar_t, char16_t, char32_t).
There is two way to get CharType*.
use const_cast. However, this is very dirty and break type system.
use &s[0]. operator[] does not break type system.
#include <string>
#include <type_traits>
int main(){
std::string s1 = "Hello world";
static_assert(std::is_same<char&, decltype(s1[0])>::value, "err");
static_assert(std::is_same<char*, decltype(&s1[0])>::value, "err");
const std::string s2 = "Hello world";
static_assert(std::is_same<const char&, decltype(s2[0])>::value, "err");
static_assert(std::is_same<const char*, decltype(&s2[0])>::value, "err");
}

Is it safe to overload char* and std::string?

I have just read about the overloading functions on a beginner book.
Just out of curiosity I 'd like to ask whether it is safe to overload between char* and std::string.
I played with the below code and get some result. But I was not sure whether it is an undefined behavior.
void foo(std::string str) {
cout << "This is the std::string version. " << endl;
}
void foo(char* str) {
cout << "This is the char* version. " << endl;
}
int main(int argc, char *argv[]) {
foo("Hello"); // result shows char* version is invoked
std::string s = "Hello";
foo(s); // result shows std::string version
return 0;
}
Yes, it's safe, as long as you make it const char*, and actually often useful. String literals cannot be converted to char* since C++11 (and it was deprecated before that).
The const char* overload will be picked for a string literal because a string literal is a const char[N] (where N is the number of characters). Overloads have a kind of priority ordering over which one will be picked when multiple would work. It's considered a better match to perform array-to-pointer conversion than to construct a std::string.
Why can overloading std::string and const char* be useful? If you had, for example, one overload for std::string and one for an bool, the bool would get called when you passed a string literal. That's because the bool overload is still considered a better match than constructing a std::string. We can get around this by providing a const char* overload, which will beat the bool overload, and can just forward to the std::string overload.
Short Answer: Perfectly safe. Consider the following uses:
foo("bar");//uses c string
foo(std::string("bar") );//uses std::string
char* bar = "bar";
foo(bar);//uses c string
std::string bar_string = "bar";
foo(bar_string);//uses std::string
foo(bar_string.c_str()); //uses c string
Word of warning, some compilers (namely those with c++11 enabled) require the const keyword in parameter specification in order to allow temporary strings to be used.
For instance, in order to get this:
foo("bar");
You need this:
void foo(const char* bar);

How do I assign a char* to a char array?

Compiler tell me "incompatibles type in assignments of char* to char[32]"
this is my code:
char* generalOperations[2]={"Off","On"};
void test(){
char value[32];
switch(swapVariable){
case 0:
value=generalOperations[0]; //<==Error HERE!
break;
}
}
[Solved]:
strcpy(value,generalOperations[0]);
Use std::string instead of char* and std::array<T, N> instead of T[N]. Both are type safe (as opposed to memcpy), both are in modern C++ style and both are directly assignable using the assignment operator.
#include <array>
#include <string>
std::array<std::string, 2> generalOperations{"Off", "On"};
void test() {
std::string value;
switch(swapVariable) {
case 0: value = generalOperations[0]; break;
}
}
You can't assign arrays. You can either change the type of value to a char* or copy the content of generalOptions[0] into value. If you are going to copy the content, then you need to ensure that value has enough space to hold the content of the element in generalOperations.
Modifying a string literal is undefined behaviour, by changing the type to const char* the compiler can detect any attempt to modify one of the entries in generalOperations instead of experiencing odd behaviour at runtime:
const char* generalOperations [2]={"Off","On"};
const char* value;
Note you don't have to specify the number of elements in the array if you are initialising it:
const char* generalOperations [] = {"Off","On"};
Or, if this really is C++ you can make value a std::string instead and just assign to it which will copy the generalOperations element.
As C++ appears to really be the language and C++11 features are permitted instead of using a switch you could create a std::map that associates the int values with the std::string:
#include <iostream>
#include <string>
#include <map>
const std::map<int, std::string> generalOperations{ {17, "Off"},
{29, "On" } };
int main()
{
auto e = generalOperations.find(17);
if (e != generalOperations.end())
{
// Do something with e->second.
std::cout << e->second << "\n";
}
return 0;
}
Demo: http://ideone.com/rvFxH.
#include <string.h>
...
strcpy(value, generalOptions[0]);
You cannot assign arrays in C/C++. There are functions do to that for you. If your char array represents a C style string (i.e. a null terminated sequence of characters), then there are more specialist functions for that as well. strcpy is one of those functions.
Your assignment is wrong, since you cannot assign a char * to char array instead of using this assignment you can use strcpy().

const string vs. #define

i need to share some strings in my c++ program. should i use #define or const string? thanks
mystring1.h
#define str1 "str1"
#define str2 "str2"
Or
mystring2.h
extern const string str1;
extern const string str2;
mystring.cpp
const string str1 = "str1";
const string str2 = "str2";
Prefer the second option. If you use the first option (preprocessor), you are limiting your flexibility with the object.
Consider the following... You won't be able to compare strings this way:
if (str1 == "some string")
{
// ...
}
If it is C++ instead of C, you should really use some variable instead of a preprocessor macro. The former is clearer than the latter. Furthermore, if you use C++17, you can use inline variables:
inline const std::string str = "foobar";
or
// constexpr is implicitly inline
constexpr char str0[] = "foobar";
constexpr const char* str1 = "foobar";
constexpr std::string_view str2 = "foobar";
This is also clearer than using extern and can be used in header-only APIs as well.
If it's C++, you should use the C++ Standard Library's std::string. It's much more clear than a preprocessor macro, it will have a single location in memory when it's defined, and it has all the extra functionality of std::string instead of only pointer comparisons as is the case with the implicit const char* that are created with a preprocessor macro.
To take OO advantage of c++, I would say use struct/class.
header:
struct Constants {
static const string s1;
static const string s2;
};
cpp:
const string Constants::s1 = "blah1";
const string Constants::s2 = "blah2";
To reference:
cout << Constants::s1 << endl;
If you don't have to use the preprocessor don't!
If these strings are needed in a resource editor or a manifest or something you might have to.
You could also just use a const char* string for constant data and not a string object, since the object will need to be initialised at the start of the program with the constant data anyway. Do this if you're not going to be doing much with strings but just displaying them or printing them out as is.
So:
extern const char * str1;
and
const char * str1 = "str1";
I would suggest use of functions.
extern const std::string& str1();
extern const std::string& str2();
This gives you more flexibility in how you get those strings in the .cpp file.
Also consider the issue of non-POD static construction and destruction order, as described in the Google C++ style guide.
An alternative is to use:
const char str1[] = "str1";
const char str1[] = "str2";