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.
Related
I have a class SpecialString. It has an operator overload / conversion function it uses any time it's passed off as a const char*. It then returns a normal c-string.
class SpecialString
{
...
operator char* () const { return mCStr; }
...
};
This used to work a long time ago (literally 19 years ago) when I passed these directly into printf(). The compiler was smart enough to know that argument was meant to be a char* and it used the conversion function, but the now g++ complains.
SpecialString str1("Hello"), str2("World");
printf("%s %s\n", str1, str2);
error: cannot pass object of non-POD type 'SPECIALSTRING' (aka 'SpecialString') through variadic method; call will abort at runtime [-Wnon-pod-varargs]
Is there any way to get this to work again without changing the code? I can add a deref operator overload function that returns the c-string and pass the SpecialString objects around like this.
class SpecialString
{
...
operator CHAR* () const { return mCStr; }
char* operator * () const { return mCStr; }
...
};
SpecialString str1("Hello"), str2("World");
printf("%s %s\n", *str1, *str2);
But I'd prefer not to because this requires manually changing thousands of lines of code.
You could disable the warning, if you don't want to be informed about it... but that's a bad idea.
The behaviour of the program is undefined, you should fix it and that requires changing the code. You can use the exising conversion operator with static_cast, or you can use your unary * operator idea, which is going to be terser.
Even less change would be required if you used unary + instead which doesn't require introducing an overload, since it will invoke the implicit conversion instead. That may add some confusion to the reader of the code though.
Since you don't want to modify the existing code, you can write a "hack" instead. More specifically, a bunch of overloads to printf() that patch the existing code.
For example:
int printf(const char* f, const SpecialString& a, const SpecialString& b)
{
return printf(f, (const char*)a, (const char*)b);
}
With this function declared in your header, every call to printf() with those specific parameters will use this function instead of the "real" printf() you're familiar with, and perform the needed conversions.
I presume you have quite a few combinations of printf() calls in your code envolving SpecialString, so you may have to write a bunch of different overloads, and this is ugly af to say the least but it does fit your requirement.
As mentioned in another comment, it has always been undefined behavior that happens to work in your case.
With Microsoft CString class, it seems like the undefined behavior was so used (as it happen to work), that now the layout is defined in a way that it will still works. See How can CString be passed to format string %s?.
In our code base, I try to fix code when I modify a file to explicitly do the conversion by calling GetString()
There are a few things you could do:
Fix existing code everywhere you get the warning.
In that case, a named function like c_str or GetString is preferable to a conversion operator to avoid explicit casting (for ex. static_cast or even worst C-style case (const char *). The deref operator might be an acceptable compromise.
Use some formatting library
<iosteam>
fmt: https://github.com/fmtlib/fmt
many other choices (search C++ formatting library or something similar)
Use variadic template function so that conversion could be done.
If you only use a few types (int, double, string) and rarely more than 2 or 3 parameters, defining overloads might also be a possibility.
Not recommended: Hack your class to works again.
Have you done any change to your class definition that cause it to break or only upgrade the compiler version or change compiler options?
Such hack is working with undefined behavior so you must figure out how your compiler works and the code won't be portable.
For it to works the class must have the size of a pointer and the data itself must be compatible with a pointer. Thus essentially, the data must consist of a single pointer (no v-table or other stuff).
Side note: I think that one should avoid defining its own string class. In most case, standard C++ string should be used (or string view). If you need additional functions, I would recommend your to do write stand-alone function in a namespace like StringUtilities for example. That way, you avoid converting back and forth between your own string and standard string (or some library string like MFC, Qt or something else).
I've recently ran into a very pesky problem working with C++03.
I needed to create a new string class that expands on the abilities of std::string, providing additional methods and more convenient handling.
All was going well except for one issue: If some function expected a MyString parameter and was given a string literal (i.e. "Hello") it couldn't do the conversion from const char[6] into MyString, despite the fact that MyString has a c'tor that accepts std::string.
For example, these lines will work:
MyString mStr("Hello");
MyString mStr = "Hello";
But this code will not work:
void myFunc(const MyString& mStr);
myFunc("Hello");
Because "no constructor exists that can perform the conversion from const char[6] into MyString".
Adding a c'tor that accepts const char * didn't help solve the problem, and even trying to mark them with explicit in different combinations didn't help at all.
After spending quite a few hours on this error, I got to the conclusion that it might not be possible to do such a thing because it may require two implicit conversions which the compiler will not perform, however as I said, adding a c'tor from const char * to enable conversion in 1 step didn't change anything. So before I'm giving up on this, do any of you have any ideas if it's even possible to solve this problem?
As for MyString's constructor taking std::string const&, the compiler can not convert from const char* through std::string to MyString, in order to call myFunc, because that's two implicit conversions, while it can do only one. See this post.
In case of const char* being the parameter, I could not reproduce your erorrs. In addition, making a constructor explicit will do no good here, it will ban even that one implicit conversion that was allowed. See it here.
Defining constructor with parameter "const char *"(MyString(const char *)) should resolve this issue.
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.
I have a class that defined a user defined operator for a TCHAR*, like so
CMyClass::operator const TCHAR*() const
{
// returns text as const TCHAR*
}
I want to be able to do something like
CMyClass myClass;
_tprintf(_T("%s"), myClass);
or even
_tprintf(_T("%s"), CMyClass(value));
But when trying, printf always prints (null) instead of the value. I have also tried a normal char* operator, as well variations with const etc.
It only works correctly if I explicitly call the operator or do a cast, like
_tprintf(_T("%s\n"), (const TCHAR*)myClass);
_tprintf(_T("%s\n"), myClass.operator const TCHAR *());
However, I don't want to cast. How can this be achieved?
Note, that a possibility is to create a function that has a parameter of const TCHAR*, so that it forcible calls the operator TCHAR*, but this I also don't want to implement.
The C++ Standard says that implicit conversions like this are not applied to ellipsis parameters - how would the compiler know what conversion to apply? You will have to perform the conversion explicitly yourself, or better yet stop using printf.
Avoid conversion operators. They rarely do what you want, and then explicit calls are painful. Rename operator const TCHAR*() const to TCHAR *str() const.
Conversion operators are called when the compiler wants to convert a value to another type. This works for functions that take defined parameters of specific types. It doesn't work for variadic functions like printf() with ... in the function declaration. These functions take the arguments and then work with them, so the conversion operator is never called.
To be specific, when the compiler sees printf("%s", foo), it passes foo, whatever it is, to printf(), which will have to assume it's suitable for a %s format as it is. No conversion operator will be called (although certain arithmetic promotions will take place).
Conversion operators in general cause problems. By having that operator in that class, you've complicated function overload resolution, since the compiler can interpret a CMyClass as if it were a TCHAR *. This can cause unexpected results, either causing code to compile when you really didn't want it to, or selecting the wrong overloaded function. (For example, given CMyClass cmc;, the expression cmc + 10 is suddenly legal, since TCHAR * + int is perfectly legitimate pointer arithmetic.) The usual practice is to label such conversions explicit.
Casting is the right thing to do if you want to use printf style APIs and rely on a conversion operator (and I am not going to argue here whether or not you should use these features). However, I would use static cast, e.g. _tprintf(_T("%s\n"), static_cast<const TCHAR*>(myClass));
In C++ is it possible to define conversion operators which are not class members? I know how to do that for regular operators (such as +), but not for conversion operators.
Here is my use case: I work with a C Library which hands me out a PA_Unichar *, where the library defines PA_Unichar to be a 16-bit int. It is actually a string coded in UTF-16. I want to convert it to a std::string coded in UTF-8. I have all the conversion code ready and working, and I am only missing the syntactic sugar that would allow me to write:
PA_Unichar *libOutput = theLibraryFunction();
std::string myString = libOutput;
(usually in one line without the temp variable).
Also worth noting:
I know that std::string doesn't define implicit conversion from char* and I know why. The same reason might apply here, but that's beside the point.
I do have a ustring, subclass of std::string that defines the right conversion operator from PA_Unichar*. It works but this means using ustring variables instead of std::string and that then requires conversion to std::string when I use those strings with other libraries. So that doesn't help very much.
Using an assignment operator doesn't work as those must be class members.
So is it possible to define implicit conversion operators between two types you don't control (in my case PA_Unichar* and std::string), which may or may not be class types?
If not what could be workarounds?
What's wrong with a free function?
std::string convert(PA_Unichar *libOutput);
std::string myString = convert(theLibraryFunction());
Edit answering to the comment:
As DrPizza says: Everybody else is trying to plug the holes opened up by implicit conversions through replacing them with those explicit conversion which you call "visual clutter".
As to the temporary string: Just wait for the next compiler version. It's likely to come with rvalue references and its std::string implementation will implement move semantics on top of that, which eliminates the copy. I have yet to see a cheaper way to speedup your code than than by simply upgrading to a new compiler version.
I don't think you can define "global" conversion operators. The standards say that conversion functions are special member functions. I would propse the following if I could consider the following syntax sugar:
struct mystring : public string
{
mystring(PA_Unichar * utf16_string)
{
// All string functionality is present in your mystring.
// All what you have to do is to write the conversion process.
string::operator=("Hello World!");
string::push_back('!');
// any string operation...
}
};
Be aware that polymorphic behavior of this class is broken. As long as you don't create an object of it through a pointer of type string* though, you are in the safe-side! So, this code is perfect:
mystring str(....);
As said before, the following code is broken!
string* str = new mystring(....);
....
delete str; // only deleting "string", but not "mystring" part of the object
// std::string doesn't define virtual destructor
Implicit conversions are the devil, anyway. Make it explicit with a converting function call.
No, you can't. What you could do as an alternative is to create a conversion constructor in the target class (not your case, as you want to convert to std::string - unless you derive it). But I agree to the other answers, I think an implicit conversion is not recommended in this case - especially because you're not converting from an object but from a pointer. Better to have a free function, your code will be easier to understand and the next programmer to inherit the code will for sure thank you.