So I was updating some code to use explicit and ran into seemingly inconsistent behavior in something like this:
explicit Whatever(const wchar_t* const pszVal)
This doesn't work with a literal string which has a type of const wchar_t[x], and explicit prevents it from being accepted in the above constructor. It complains that
const wchar_t[x] cannot be converted to const wchar_t* const
.
But, if I add one for the array scenario:
explicit Whatever(const wchar_t pszVal[])
Then it claims this constructor already exists and that it is the above constructor that won't accept the array form. So it seems like a catch-22 that would prevent me from having an explicit constructor for literal strings.
This is Visual C++ 2017. Am I missing something here? I'd be happy to provide the extra constructor of course, but it appears to be acting inconsistently.
OK, just to follow up, since this wouldn't work in a comment very easily, the scenarios is this:
class AString { Whatever(const wchar_t* const val) {} } ;
class Something { Something(const AString& val) {} };
Creating a Something passing a literal fails, because it won't convert L"X" into AString :
Something MySomething(L"X");
Obviously is a different thing from what I was thinking, but unexpected to me. AString has a valid constructor to take a wchar_t. Why wouldn't the above just create a temporary?
If that's how explicit really is supposed to work, it's sort of semi-useless since you could never make any fundamental classes of this sort explicit if you wanted to take parameters of that type but pass in things that you explicitly said you want that class to accept, right?
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 stumbled upon something the other day in the code of a colleague and I want to understand how/why this works.
He uses a struct like this
struct my_struct
{
my_struct(){ /* default constructor*/};
my_struct(char c){ /*some special constructor*/};
// other stuff
my_struct& operator=(const my_struct &ms){ /* assignment */};
};
Originally through a typo he figured out that the following works
my_struct ms;
double a;
ms = a;
I have worked out that this is equal (in the sense of gives the same final struct ms) to the following
my_struct ms;
double a;
my_struct ms2((char) a);
ms=ms2;
But I don't know why. Personally, I think this should not work, because there is no assign operator for double and my_struct and additionally because there is no constructor for my_struct using a double.
I tried to google this, but did not find anything relevant.
# user657267: i wanted to keep it as short as possible, you could add a line a=5; or whatever the same would apply.
During the compilation this:
ms = a;
attempts to resolve an assignment of one or more of the following immediate forms
my_struct& operator ()(double)
my_struct& operator ()(double&)
my_struct& operator ()(const double&)
and a litany of other potential conversion from double. And none is found. In fact, only the following is discovered:
my_struct& operator=(const my_struct &ms)
which indicates the compiler does allow assignment via a const-reference of another my_struct. At that time a conversion constructor is sought, starting with the most immediate conversion (explicit to the type double:
my_struct(double)
my_struct(double&)
my_struct(const double&)
It is important to note such a constructor cannot mandate explicit use. We're implicitly constructing a temporary, and as such explicit mandate is off the reservation.
Anyway, again, none is found. Before giving up the compiler attempts to locate a constructor of any form that can convert from double to whatever the fundamental construction requires and said constructor supports implicit construction. In effect, something of the form
my_struct(<<something that can be converted from a double>>)
And again, the constructor cannot be explicit. As it turns out, this search does find this:
my_struct(char)
because double can be converted to char (albeit poorly and likely not what you want). The entire chain is a little (ok, a lot) more complicated than that, but that is the crux of it.
You can avoid this implicit construction by ensuring that constructor is only explicit in usage, but that is a double-edge sword, so care is required. Use of explicit can be somewhat harsh. Doing this:
explicit my_struct(char)
will squelch the unintended implicit construction when converting double to char. But I warn you; it will also squelch the ability to do this:
char x = 'a';
my_struct ms;
ms = x;
because now my_struct cannot be implicitly constructed at all from char, by conversion or otherwise. There are multiple ways around this, including defining a specific assignment operator for the type (takes a double) or a specific conversion constructor (takes a double). How you approach that I leave to you.
You have yourself a conversion constructor:
my_struct(char c)
This means anything that can be converted to char can be used to implicitly create an object of my_struct.
In order to avoid this you can add explicit to your constructor:
explicit my_struct(char c)
This way you can create an object from double like this:
double a = my_initial_value;
my_struct ms2(a);
Also, always initialize your variables, otherwise types like int, float, pointers etc. will hold random values.
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 understand that the keyword explicit can be used to prevent implicit conversion.
For example
Foo {
public:
explicit Foo(int i) {}
}
My question is, under what condition, implicit conversion should be prohibited? Why implicit conversion is harmful?
Use explicit when you would prefer a compiling error.
explicit is only applicable when there is one parameter in your constructor (or many where the first is the only one without a default value).
You would want to use the explicit keyword anytime that the programmer may construct an object by mistake, thinking it may do something it is not actually doing.
Here's an example:
class MyString
{
public:
MyString(int size)
: size(size)
{
}
//... other stuff
int size;
};
With the following code you are allowed to do this:
int age = 29;
//...
//Lots of code
//...
//Pretend at this point the programmer forgot the type of x and thought string
str s = x;
But the caller probably meant to store "3" inside the MyString variable and not 3. It is better to get a compiling error so the user can call itoa or some other conversion function on the x variable first.
The new code that will produce a compiling error for the above code:
class MyString
{
public:
explicit MyString(int size)
: size(size)
{
}
//... other stuff
int size;
};
Compiling errors are always better than bugs because they are immediately visible for you to correct.
It introduces unexpected temporaries:
struct Bar
{
Bar(); // default constructor
Bar( int ); // value constructor with implicit conversion
};
void func( const Bar& );
Bar b;
b = 1; // expands to b.operator=( Bar( 1 ));
func( 10 ); // expands to func( Bar( 10 ));
A real world example:
class VersionNumber
{
public:
VersionNumber(int major, int minor, int patch = 0, char letter = '\0') : mMajor(major), mMinor(minor), mPatch(patch), mLetter(letter) {}
explicit VersionNumber(uint32 encoded_version) { memcpy(&mLetter, &encoded_version, 4); }
uint32 Encode() const { int ret; memcpy(&ret, &mLetter, 4); return ret; }
protected:
char mLetter;
uint8 mPatch;
uint8 mMinor;
uint8 mMajor;
};
VersionNumber v = 10; would almost certainly be an error, so the explicit keyword requires the programmer to type VersionNumber v(10); and - if he or she is using a decent IDE - they will notice through the IntelliSense popup that it wants an encoded_version.
Mostly implicit conversion is a problem when it allows code to compile (and probably do something strange) in a situation where you did something you didn't intend, and would rather the code didn't compile, but instead some conversion allows the code to compile and do something strange.
For example, iostreams have a conversion to void *. If you're bit tired and type in something like: std::cout << std::cout; it will actually compile -- and produce some worthless result -- typically something like an 8 or 16 digit hexadecimal number (8 digits on a 32-bit system, 16 digits on a 64-bit system).
At the same time, I feel obliged to point out that a lot of people seem to have gotten an almost reflexive aversion to implicit conversions of any kind. There are classes for which implicit conversions make sense. A proxy class, for example, allows conversion to one other specific type. Conversion to that type is never unexpected for a proxy, because it's just a proxy -- i.e. it's something you can (and should) think of as completely equivalent to the type for which it's a proxy -- except of course that to do any good, it has to implement some special behavior for some sort of specific situation.
For example, years ago I wrote a bounded<T> class that represents an (integer) type that always remains within a specified range. Other that refusing to be assigned a value outside the specified range, it acts exactly like the underlying intger type. It does that (largely) by providing an implicit conversion to int. Just about anything you do with it, it'll act like an int. Essentially the only exception is when you assign a value to it -- then it'll throw an exception if the value is out of range.
It's not harmful for the experienced. May be harmful for beginner or a fresher debugging other's code.
"Harmful" is a strong statement. "Not something to be used without thought" is a good one. Much of C++ is that way (though some could argue some parts of C++ are harmful...)
Anyway, the worst part of implicit conversion is that not only can it happen when you don't expect it, but unless I'm mistaken, it can chain... as long as an implicit conversion path exists between type Foo and type Bar, the compiler will find it, and convert along that path - which may have many side effects that you didn't expect.
If the only thing it gains you is not having to type a few characters, it's just not worth it. Being explicit means you know what is actually happening and won't get bit.
To expand Brian's answer, consider you have this:
class MyString
{
public:
MyString(int size)
: size(size)
{
}
// ...
};
This actually allows this code to compile:
MyString mystr;
// ...
if (mystr == 5)
// ... do something
The compiler doesn't have an operator== to compare MyString to an int, but it knows how to make a MyString out of an int, so it looks at the if statement like this:
if (mystr == MyString(5))
That's very misleading since it looks like it's comparing the string to a number. In fact this type of comparison is probably never useful, assuming the MyString(int) constructor creates an empty string. If you mark the constructor as explicit, this type of conversion is disabled. So be careful with implicit conversions - be aware of all the types of statements that it will allow.
I use explicit as my default choice for converting (single parameter or equivalent) constructors. I'd rather have the compiler tell me immediately when I'm converting between one class and another and make the decision at that point if the conversion is appropriate or instead change my design or implementation to remove the need for the conversion completely.
Harmful is a slightly strong word for implicit conversions. It's harmful not so much for the initial implementation, but for maintenance of applications. Implicit conversions allow the compiler to silently change types, especially in parameters to yet another function call - for example automatically converting an int into some other object type. If you accidentally pass an int into that parameter the compiler will "helpfully" silently create the temporary for you, leaving you perplexed when things don't work right. Sure we can all say "oh, I'll never make that mistake", but it only takes one time debugging for hours before one starts thinking maybe having the compiler tell you about those conversions is a good idea.