I was wondering if there is a difference between the methods of conversion, and if one is better than the other.
Suppose there is a class A and a class B. class B can be converted to a class A. Say for example class A is something representing a string, and class B is something representing an integer.
You could obtain a class A object from a class B object in a number of ways:
operator A() function in class B to allow implicit conversion
explicit operator A() function in class B to allow explicit conversion
A toA() function in class B, to effectively convert it
static A parse(B) function in class A to convert it
A(B) constructor in class A to create a new object
Is there any preferred way of converting the object? Should multiple ways be implemented? Should any be avoided? Or is it all context-based, and should it be determined by best judgment?
It's been a while since I posted this question and since then I've developed a few guidelines which I try to stick by in my projects, and I thought I'd share them so others who stumble upon this question can benefit from them as well.
I also welcome any input on the drawbacks of these guidelines, as they are probably not perfect.
An object should only provide implicit conversion constructors for objects that are directly related to that class in some way. For example, a string class that offers a constructor that accepts a char array. When the conversion is lossy, or the classes are unrelated, use an explicit conversion constructor.
An object should only provide implicit conversion operators to objects it is directly related to and for conversions which are lossless (no information is lost). Again, a string can provide a conversion to a character array. If this is not the case, an explicit conversion operator should be used.
Provide toX() functions for all conversion operators (implicit and explicit). This isn't really a strict guideline, but more personal preference. Sometimes it is better to write toX() in your code as it conveys your intent better, in my opinion.
Only use parse(X) functions if the function has to be static for some reason.
Free functions can be used as an alternative to a parse(X) function, this depends on personal preference.
As I said, I welcome any input and I hope this may help someone.
Related
Recently I have to work with some C libraries in my C++ code. The C library I am using defined a complex number class as follow:
typedef struct sfe_complex_s
{
float real;
float img;
} sfe_complex_t;
Naturally I do not want to work with this C-style data structure in C++, so for convenience I want to define an implicit conversion from this type to std::complex<float>. Is there a way to do so? Or I have to explicitly do the conversion?
Implicit conversion is supposed to mean something. It represents a strong relationship between the source and destination types of that conversion. That one of them is, on some level, designed to be equivalent to another in some degree.
As such, only code which is intimately associated with either the source or destination type can define that relationship. That is, if you don't have control over the source or destination types, then C++ doesn't feel that you are qualified to create an implicit conversion relationship between them.
3rd parties cannot make types implicitly convertible.
An implicit conversion can only take place in a constructor of the class converted to or a conversion operator of the class converted from, which has to be a member function. Since you cannot modify either one, you are out of luck.
It may be appealing to define an own class with implicit constructors from and conversion operators to both std::complex<float> and sfe_complex_t, but this will not give you implicit conversions between these two clases, since user defined conversions cannot be chained. You could a class defined in this way in your code and it would always be converted implicitly if you put it somewhere either of the others are expected, but you should really consider if that is what you want. It would introduce another standard and make your code less readable.
To me, it seems like calling an explicit conversion is the cleanest variant. Depending on the number of functions of your C library you could perhaps for each one provide an overload taking std::complex and explicitly converting it before passing it on.
No, you cannot define an implicit conversion between types that you don't have access to change. Your only option will be to define your own function that explicitly takes a sfe_complex_t as input and returns a std::complex<float> as output.
I am using a library that has some useful types but they are missing some functionality. I am thus thinking if there is a good way to extend these types.
I have taken some other types from the library, inherited from them and just used my own expanded versions. I can't think of a way to make this work for the following issue.
There is a floating point and an integral 2D point struct, i.e. Point2D and Point2DInt. I would like to be able to either implicitly convert between the two or be able to construct one from the other, in either case I want to add a constructor to this external class.
There is a float to int constructor in the integral class but none that goes the other way.
I understand that I could just make a utility function to do this but would like to know if this is possible in general.
The only way of making a class implicitly convertible to another is to modify one of the classes (either add converting constructor to one, or conversion operator to another). The modification has to be in the definition of the class; not after the definition.
Indeed, you can write a function that takes one as an argument and returns the other, but the implicit conversion is not possible.
I'm using two independent third party libraries in my C++ project (Qt, cocos2d). Both define their own version of 2d and 3d vectors. How could I implement custom implicit (or explicit) conversion methods, so that I don't have to explicitly create new instances like this:
// mousePosition is of type QPointF
auto position = cocos2d::Vec2(mousePosition.x(), mousePosition.y())
Of course I cannot really modify the source of either library. In Obj-C or C# I could use class extensions. I'm not sure about C++. Is there any better solution than plain global functions?
There’s no way to implement an implicit conversion between two types without altering either of them. This can be explained mechanically: such a conversion would have to be a constructor or a conversion operator, both of which must be members.
The more enlightening explanation is that such an implicit conversion could break existing use of the types (e.g., by introducing ambiguity). With templates, it can easily be undefined behavior to introduce additional overloads (or, in this hypothetical case, make more viable) after the template is defined.
So your conversion must be explicit, and then it’s trivial: define
B convert(const A&);
A convert(const B&);
for as many A-B pairs as necessary; conveniently, they need not even have different names, since by hypothesis there is no implicit conversion between them.
Where I can find an excellently understandable article on C++ type conversion covering all of its types (promotion, implicit/explicit, etc.)?
I've been learning C++ for some time and, for example, virtual functions mechanism seems clearer to me than this topic. My opinion is that it is due to the textbook's authors who are complicating too much (see Stroustroup's book and so on).
(Props to Crazy Eddie for a first answer, but I feel it can be made clearer)
Type Conversion
Why does it happen?
Type conversion can happen for two main reasons. One is because you wrote an explicit expression, such as static_cast<int>(3.5). Another reason is that you used an expression at a place where the compiler needed another type, so it will insert the conversion for you. E.g. 2.5 + 1 will result in an implicit cast from 1 (an integer) to 1.0 (a double).
The explicit forms
There are only a limited number of explicit forms. First off, C++ has 4 named versions: static_cast, dynamic_cast, reinterpret_cast and const_cast. C++ also supports the C-style cast (Type) Expression. Finally, there is a "constructor-style" cast Type(Expression).
The 4 named forms are documented in any good introductory text. The C-style cast expands to a static_cast, const_cast or reinterpret_cast, and the "constructor-style" cast is a shorthand for a static_cast<Type>. However, due to parsing problems, the "constructor-style" cast requires a singe identifier for the name of the type; unsigned int(-5) or const float(5) are not legal.
The implicit forms
It's much harder to enumerate all the contexts in which an implicit conversion can happen. Since C++ is a typesafe OO language, there are many situations in which you have an object A in a context where you'd need a type B. Examples are the built-in operators, calling a function, or catching an exception by value.
The conversion sequence
In all cases, implicit and explicit, the compiler will try to find a conversion sequence. A conversion sequence is a series of steps that gets you from type A to type B. The exact conversion sequence chosen by the compiler depends on the type of cast. A dynamic_cast is used to do a checked Base-to-Derived conversion, so the steps are to check whether Derived inherits from Base, via which intermediate class(es). const_cast can remove both const and volatile. In the case of a static_cast, the possible steps are the most complex. It will do conversion between the built-in arithmetic types; it will convert Base pointers to Derived pointers and vice versa, it will consider class constructors (of the destination type) and class cast operators (of the source type), and it will add const and volatile. Obviously, quite a few of these step are orthogonal: an arithmetic type is never a pointer or class type. Also, the compiler will use each step at most once.
As we noted earlier, some type conversions are explicit and others are implicit. This matters to static_cast because it uses user-defined functions in the conversion sequence. Some of the conversion steps consiered by the compiler can be marked as explicit (In C++03, only constructors can). The compiler will skip (no error) any explicit conversion function for implicit conversion sequences. Of course, if there are no alternatives left, the compiler will still give an error.
The arithmetic conversions
Integer types such as char and short can be converted to "greater" types such as int and long, and smaller floating-point types can similarly be converted into greater types. Signed and unsigned integer types can be converted into each other. Integer and floating-point types can be changed into each other.
Base and Derived conversions
Since C++ is an OO language, there are a number of casts where the relation between Base and Derived matters. Here it is very important to understand the difference between actual objects, pointers, and references (especially if you're coming from .Net or Java). First, the actual objects. They have precisely one type, and you can convert them to any base type (ignoring private base classes for the moment). The conversion creates a new object of base type. We call this "slicing"; the derived parts are sliced off.
Another type of conversion exists when you have pointers to objects. You can always convert a Derived* to a Base*, because inside every Derived object there is a Base subobject. C++ will automatically apply the correct offset of Base with Derived to your pointer. This conversion will give you a new pointer, but not a new object. The new pointer will point to the existing sub-object. Therefore, the cast will never slice off the Derived part of your object.
The conversion the other way is trickier. In general, not every Base* will point to Base sub-object inside a Derived object. Base objects may also exist in other places. Therefore, it is possible that the conversion should fail. C++ gives you two options here. Either you tell the compiler that you're certain that you're pointing to a subobject inside a Derived via a static_cast<Derived*>(baseptr), or you ask the compiler to check with dynamic_cast<Derived*>(baseptr). In the latter case, the result will be nullptr if baseptr doesn't actually point to a Derived object.
For references to Base and Derived, the same applies except for dynamic_cast<Derived&>(baseref) : it will throw std::bad_cast instead of returning a null pointer. (There are no such things as null references).
User-defined conversions
There are two ways to define user conversions: via the source type and via the destination type. The first way involves defining a member operator DestinatonType() const in the source type. Note that it doesn't have an explicit return type (it's always DestinatonType), and that it's const. Conversions should never change the source object. A class may define several types to which it can be converted, simply by adding multiple operators.
The second type of conversion, via the destination type, relies on user-defined constructors. A constructor T::T which can be called with one argument of type U can be used to convert a U object into a T object. It doesn't matter if that constructor has additional default arguments, nor does it matter if the U argument is passed by value or by reference. However, as noted before, if T::T(U) is explicit, then it will not be considered in implicit conversion sequences.
it is possible that multiple conversion sequences between two types are possible, as a result of user-defined conversion sequences. Since these are essentially function calls (to user-defined operators or constructors), the conversion sequence is chosen via overload resolution of the different function calls.
Don't know of one so lets see if it can't be made here...hopefully I get it right.
First off, implicit/explicit:
Explicit "conversion" happens everywhere that you do a cast. More specifically, a static_cast. Other casts either fail to do any conversion or cover a different range of topics/conversions. Implicit conversion happens anywhere that conversion is happening without your specific say-so (no casting). Consider it thusly: Using a cast explicitly states your intent.
Promotion:
Promotion happens when you have two or more types interacting in an expression that are of different size. It is a special case of type "coercion", which I'll go over in a second. Promotion just takes the small type and expands it to the larger type. There is no standard set of sizes for numeric types but generally speaking, char < short < int < long < long long, and, float < double < long double.
Coercion:
Coercion happens any time types in an expression do not match. The compiler will "coerce" a lesser type into a greater type. In some cases, such as converting an integer to a double or an unsigned type into a signed type, information can be lost. Coercion includes promotion, so similar types of different size are resolved in that manner. If promotion is not enough then integral types are converted to floating types and unsigned types are converted to signed types. This happens until all components of an expression are of the same type.
These compiler actions only take place regarding raw, numeric types. Coercion and promotion do not happen to user defined classes. Generally speaking, explicit casting makes no real difference unless you are reversing promotion/coercion rules. It will, however, get rid of compiler warnings that coercion often causes.
User defined types can be converted though. This happens during overload resolution. The compiler will find the various entities that resemble a name you are using and then go through a process to resolve which of the entities should be used. The "identity" conversion is preferred above all; this means that a f(t) will resolve to f(typeof_t) over anything else (see Function with parameter type that has a copy-constructor with non-const ref chosen? for some confusion that can generate). If the identity conversion doesn't work the system then goes through this complex higherarchy of conversion attempts that include (hopefully in the right order) conversion to base type (slicing), user-defined constructors, user-defined conversion functions. There's some funky language about references which will generally be unimportant to you and that I don't fully understand without looking up anyway.
In the case of user type conversion explicit conversion makes a huge difference. The user that defined a type can declare a constructor as "explicit". This means that this constructor will never be considered in such a process as I described above. In order to call an entity in such a way that would use that constructor you must explicitly do so by casting (note that syntax such as std::string("hello") is not, strictly speaking, a call to the constructor but instead a "function-style" cast).
Because the compiler will silently look through constructors and type conversion overloads during name resolution, it is highly recommended that you declare the former as 'explicit' and avoid creating the latter. This is because any time the compiler silently does something there's room for bugs. People can't keep in mind every detail about the entire code tree, not even what's currently in scope (especially adding in koenig lookup), so they can easily forget about some detail that causes their code to do something unintentional due to conversions. Requiring explicit language for conversions makes such accidents much more difficult to make.
For integer types, check the book Secure Coding n C and C++ by Seacord, the chapter about integer overflows.
As for implicit type conversions, you will find the books Effective C++ and More Effective C++ to be very, very useful.
In fact, you shouldn't be a C++ developer without reading these.
Why does C++ require that user-defined conversion operator can only be non-static member?
Why is it not allowed to use standalone functions as for other unary operators?
Something like this:
operator bool (const std::string& s) { return !s.empty(); }
The one reason I can think of is to prevent implicit conversions being applied to the thing being cast. In your example, if you said:
bool( "foo" );
then "foo" would be implicitly converted to a string, which would then have the explicit bool conversion you provided applied to it.
This is not possible if the bool operator is a member function, as implicit conversions are not applied to *this. This greatly reduces the possibilities for ambiguity - ambiguities normally being seen as a "bad thing".
By keeping the conversion operator within the class you give the author of the class control of how it could be converted (It prevents users from creating implicit conversions). As an implementer I would consider this an advantage, as implicit conversions does have its issues
There is a difference being able to pass one object as another, and having it to go through a conversion function. The former communicates that the object is of a given type, while the latter shows new readers that there is a difference between the two types and that a conversion is necessary.
Implicit user-defined conversions are frowned upon anyway. Don't use them. Just pretend that they aren't there. Let alone thinking about newer ways to introduce them.
Anyway, I guess they aren't there because the way they are they can do enough unexpected things. Including a new header which introduces such a conversion for a class defined somewhere else might lead to even more confusing errors.
There's a group of operators that have to be overloaded as non-static member functions: assignment, subscripting, function call, class member access, conversion functions.
I guess the standard's committee or Stroustrup simply felt it might be just too confusing if it was allowed to inject these very special behaviors to classes from outside.
I suppose the best way to get the answer would be to e-mail the author.