I'm learning C++ and creating a simple class, tstring, defined by:
n the number of characters in a string;
ntotal the total number of characters in the tstring;
p, pointer to a zone which contains the data.
Hence, this class is composed by a static part (n, ntotal and p) and by a dynamic part (the data itself).
I created three constructors :
one without parameters;
one copy constructor;
one constructor which takes a C-string parameter.
I would like to be able to concatenate one tstring object with one C string, using for example the instructions :
tstring x("Hello), y;
y = x + " World";
I think I should overload the + operator. However I don't understand which way I should use to overload it between internal way :
tstring tstring::operator+(char string[])
or external way (with setter and getter methods)
tstring operator+ (const tstring& myTstring, const char* string)
Can someone explain me the difference and the best way to adopt?
Thank you in advance!
In this case, you probably should consider concatenation of two tstrings first. Having this, you may decide to allow your tstring(const char *) constructor to deal with conversion for you. This way you will get concatenation with C-strings free.
Then you'll notice that tstring + char [] does not yield the same result as char [] + tstring. That is because first will call your converting constructor and second will not find any matching operator.
As having asymmetrical operator+ is counterintuitive, you should also provide non-member operator+(const char *, tstring const &) that will forward its arguments to its in-class sibling.
Related
I tried two different ways to append an int to a std::string, and to my surprise, I got different results:
#include <string>
int main()
{
std::string s;
s += 2; // compiles correctly
s = s + 2; // compiler error
return 0;
}
Why does it compile and work correctly when I use the += operator, but fail when I use the + operator?
I don't think the question is like How to concatenate a std::string and an int?
In that question,no answer uses += operator.And the difference between +=
and + operator of std::string is the key to solve my doubt.
Frankly,the question is a good example for explaining why c++ is so difficult to master.
TL;DR operator+= is a class member function in class string, while operator+ is a template function.
The standard class template<typename CharT> basic_string<CharT> has overloaded function basic_string& operator+=(CharT), and string is just basic_string<char>.
As values that fits in a lower type can be automatically cast into that type, in expression s += 2, the 2 is not treated as int, but char instead. It has exactly the same effect as s += '\x02'. A char with ASCII code 2 (STX) is appended, not the character '2' (with ASCII value 50, or 0x32).
However, string does not have an overloaded member function like string operator+(int), s + 2 is not a valid expression, thus throws an error during compilation. (More below)
You can use operator+ function in string in these ways:
s = s + char(2); // or (char)2
s = s + std::string(2);
s = s + std::to_string(2); // C++11 and above only
For people concerned about why 2 isn't automatically cast to char with operator+,
template <typename CharT>
basic_string<CharT>
operator+(const basic_string<CharT>& lhs, CharT rhs);
The above is the prototype[note] for the plus operator in s + 2, and because it's a template function, it is requiring an implementation of both operator+<char> and operator+<int>, which is conflicting. For details, see Why isn't automatic downcasting applied to template functions?
Meanwhile, the prototype of operator+= is:
template <typename CharT>
class basic_string{
basic_string&
operator+=(CharT _c);
};
You see, no template here (it's a class member function), so the compiler deduces that type CharT is char from class implementation, and int(2) is automatically cast into char(2).
Note: Unnecessary code is stripped when copying from C++ standard include source. That includes typename 2 and 3 (Traits and Allocator) for template class "basic_string", and unnecessary underscores, in order to improve readability.
s += 2; is not doing what you think it's doing. It calls the overloaded += operator to a char. It does not append the character '2', but rather the character with value 2, and the result will depend on the encoding used on your platform.
There is no operator overload defined to allow s + 2 to compile1. Hence the error.
The solution in both cases is to use std::to_string(2) rather than the int literal 2.
1 Essentially the reason is because operator+= is not a template function, but std::operator+ is, and overload resolution will favour a non-template function over a template one.
The correct way to add to your string would be
std::string s;
s += std::to_string(2);
s = s + std::to_string(2);
While #CoryKramer answer gives you the correct way to add an integer to a string, it doesn't explain why the instruction s = s + 2 does not compile.
The difference between the two instruction is that in the first one you use the std::string 's += operator while in the second instruction, the compiler tries to cast 2 to a string.
There is no implicit conversion between int and std::string. however, you can cast an int to char, so this is why s += 2works.
What is the difference between the following two in C++?
fun(L"text1")
VS
std::wstring var = "text1"
fun(var)
In the first case, it is being passed as an object while the second case passes it as a wstring.
How should fun() be defined to handle both?
EDIT:
I have two function definitions
fun(void*)
fun(std::wstring)
std::wstring t = "bla";
fun(t);
fun(L"msg");
When fun(t) is called it goes to the definition of fun(std::wstring)
But when fun(L"msg") is called it goes to fun(Void*). Instead I want it to goto fun(std::wstring)
The first is passed as a wide-string literal. In the second case, you pass by value (and hence copy) or by reference an std::wstring object.
To handle both, you have to define two overloads of your function:
void fun(const wchar_t* s);
void fun(const std::wstring& s);
or you can just define the wstring version, because the literal will implicitly convert to a wstring.
To handle both you should define the fun method as:
void fun(const std::wstring& str);
then in both cases you would be passing a const reference to a wstring because the compiler is allowed to implicitly cast one type to another if the type being cast to has a constructor that takes one argument of the type being cast from unless that constructor is marked as explicit.
Example:
class wstring
{
public:
// constructor not marked as explicit and takes one argument of type whar_t*
wstring(const wchar_t* str);
};
wstring myString = L"hello world"; // implicit cast from wchar_t* to wstring
The only difference between the two examples you've given is that in the first you are passing an rvalue (which you can only bind to a const reference) and in the second you are passing an lvalue (which you can bind to both const and non-const reference).
In given examples there is no much difference, because compiler will generate constant data containing your literal and use it in both cases.
In the first case, raw literal will be used from string table of your module. This is as fast as possible code without heap allocation (actually no allocations).
In the second case, compiler will allocate string buffer in the heap, which results into malloc() call and strcpy(). This will increase time of your code and cause more memory fragmentation.
You should use std string classes only when you really need to use their useful methods, otherwise, TCHAR buffers are just excellent choice.
I am studying the C++ Primer 5th edition. On page 479, it mentions that
string sum = accumulate(v.cbegin(), v.cend(), ""); is not correct since no + on const char* for "";
The type of v is a sequential container such as a vector.
I found that using double quotes to create a string is dangerous, especially for the condition that you use generic algorithm, since it requires the objects' defined operator.
But I can't figure out what the description no + on const char* means?
Will it call the constructor string (const char* s); which is defined in c-string?
I think that's what the compiler does as it interprets the string which are initialized with quotes in C++.
Should we create a string with double quotes? It will cause overriding on operators failed.
What it's saying is that you can't add pointers to [const] char to each other, nor can you add a char to a char [const] * (well, technically, you can add a char to a char *, but it won't do what you want--it'll basically treat the char * as if it points to the beginning of an array, and treat the char as an index into that array, so the result will be a char * offset by some distance from the original, where the distance is equal to the encoded value of the char you added).
std::accumulate deduces the type it should use for the summation from the type of the value you pass as the third parameter. It tries to add things using that type, then when it's done, it attempts to return that value (then, in this case the assignment would attempt to convert char const * to std::string). That final conversion would work fine, but the intermediate additions would not.
To make it work, you need to pass an actual std::string as the initial value:
string sum = accumulate(v.cbegin(), v.cend(), string());
This way it will deduce the summation type as std::string, so it'll be adding items to a string instead of trying to add to a char const *. If you're compiler is new enough, you could get the same effect with:
string sum = accumulate(v.cbegin(), v.cend(), ""s);
C++11 added a "user defined literal" feature that allows a suffix like this to invoke a constructor for an object type. C++14 added a number of predefined suffixes like this, including 2 that use the s suffix like this--this one (for a string literal) creates a std::string. The other (for integers) creates a timing value used with the std::chrono timing classes/functions, so 3s means "three seconds".
I found that using double quotes to create string is dangerous
This is nonsense. There's no way in which it is "dangerous" to use string literals. In fact, using double quotes is pretty much the only way to create string literals, short of initializing each character separately in an array. And that is way more broken.
no + on const char* for "";
I have no idea what this means, either. I doubt it's a quote out of the book or your compiler's error messages.
I think what it's trying to say is that there is no + operator defined for string literals. That is true. They are just pointers to an array of characters. You can only overload operators for class objects. That's the advantage of std::string—you can concatenate string objects using the + operator.
If you want a string object, you will need to create one:
std::string myString("Hello");
You can concatenate two of those:
std::string myOtherString("World");
std::cout << (myString + myOtherString) << std::endl;
To answer your questions, if you want a string object, you should create one. You do not, however, always need a string object. Sometimes a C-style string literal is perfectly acceptable. It is foolish to make "rules" about what you should or should not do. It is actually quite rare that a normal application concatenates strings like this.
const char* is just a pointer to a C style array, if you try and use the + operator on 2 of them it won't compile.
String literal "" has type const char [1]. Passed to the algorithm it is adjusted to type const char *
Inside the algorithm there is used operator +. However this operator is not defined for adding two objects of type const char *. So the compiler shall issue an error.
If inside the body of the algorithm there is used an object of type std::string then the problem will be with the assignment statement because there is no assignment operator for objects of type const char * that accepts objects of type std::string as the right operands.
So it seems you should write the call of the algorithm the following way
string sum = accumulate(v.cbegin(), v.cend(), string() );
And the author of the book meant this when it said that "no + on const char*
That is if there are two pointers for example
const char *p = "Hello ";
const char *q = "World";
you may not write
q + p
I'm currently porting a C++ application to a slightly restricted environment. The application uses the STL, string and stream classes. I'm rewriting simplified versions of these that will play nicely in my environment.
What I'm concerned about is that my application is compiling even without all the necessary operator definitions. For example, for my string classes I defined:
string operator+ (const string& lhs, const string& rhs);
and this was enough. However, I noticed there were often cases that had mystring + "some constant string" and this isn't defined in my code anywhere. When I explicitly added it it was used:
string operator+ (const string& lhs, const char* rhs);
What was going on before that? It compiled successfully before I added the second function. Surely the compiler wouldn't be able to infer how to concatenate c-style strings to my string class.
I'm getting strange behaviour in my program now and I'm wondering if it's due to other operators left undefined. Is there any way to enforce the compiler to require such operator definitions if it's needed by the program?
P.S. My string class is in a unique namespace and unrelated to std::
It's impossible to be certain without seeing the rest of your code, but in this case it's probably not too hard to guess either. You almost certainly have a constructor for your string that takes a char const * as its parameter, so what's happening is that the compiler is using that ctor to convert the char const * to a string, then using string operator+ (const string& lhs, const string& rhs); to concatenate that.
Allowing this to happen is one of (if not the) primary reason for overloading these operators with globals instead of member functions. As member functions they can convert the right operand, but not the left.
When you passed a const char*, a string object was probably constructed and passed to operator+. If you step through the code in a debugger you will probably be able to verify the constructor is being called.
You probably, have a constructor in your class which takes const char * as an input parameter, Most likely this constructor is used for implicit coversions and the weird behavior you see.
Declare your constructor which takes const char * as explicit, this would disable its use for implicit conversions where you do not intend it to be used.
Does your 'string' class have a one argument constructor that takes a const char*? Is that single argument constructor marked as 'explicit'?
If it is not explicit, the the most likely answer is that the compiler is constructing a temporary string object from the char* via your converting constructor, and then using that temporary string to call your two argument addition operator.
The compiler can't know how to convert your string to a std::string or char* unless you specify how.
Look for conversion constructors or cast operators in your class declaration.
class MyString
{
MyString(char*);
MyString(std::string);
operator std::string ();
operator char*();
};
These will be called implicitly.
You can specify the explicit keyword for your constructor to prevent this from happening.
If this doesn't solve the problem, you must have the operators overloaded somewhere, best find them stepping through the code with a debugger.
I have a class called FileProc that runs File IO operations. In one instance I have declared two functions (which are sub-functions to operator= functions), both decisively different:
const bool WriteAmount(const std::string &Arr, const long S)
{
/* Do some string conversion */
return true;
}
const bool WriteAmount(const char Arr[], unsigned int S)
{
/* Do some string conversion */
return true;
}
If I make a call with a 'char string' to WriteAmount, it reports an ambiguity error - saying it is confused between WriteAmount for char string and WriteAmount for std::string. I know what is occurring under the hood - it's attempting to using the std::string constructor to implicitly convert the char string into a std::string. But I don't want this to occur in the instance of WriteAmount (IE I don't want any implicit conversion occurring within the functions - given each one is optimised to each role).
My question is, for consistency, without changing the function format (IE not changing number of arguments or what order they appear in) and without altering the standard library, is there anyway to prevent implicit conversion in the functions in question?
I forgot to add, preferably without typecasting, as this will be tedious on function calls and not user friendly.
You get the ambiguity because your second parameter is different. Trying to call it with long x = ...; WriteAmount("foo", x) will raise an ambiguity because it matches the second argument better with the first overload, but the first argument is matched better with the second overload.
Make the second parameter have the same type in both cases and you will get rid of the ambiguity, as then the second argument is matched equally worse/good for both overloads, and the first argument will be matched better with the second overload.
Can't you change the second argument and cast it to unsigned int ? It should not be able to use the first function call. I have not coded in C++ for ages..