Here in this link it says:
The static_cast keyword can be used for any normal conversion between
types. Conversions that rely on static (compile-time) type
information. This includes any casts between numeric types, casts of
pointers and references up the hierarchy, conversions with unary
constructor, conversions with conversion operator. For conversions
between numeric types no runtime checks if data fits the new type is
performed. Conversion with unary constructor would be performed even
if it is declared as explicit.
Also here:
To interoperate well with other CLS-compliant languages, you may wish
to wrap each user-defined unary constructor for a given class with a
corresponding convert-from operator.
What do they mean by "Unary Constructor"?
Unary means one, so what they are talking about is a constructor with a single parameter. The standard name for such a thing is a conversion constructor.
Unary refers to one or singular, so a 'Unary constructor' ideally refers to a constructor with a single parameter.
Related
This question is a follow-up on
Invalid operand compiler error involving std::equal_range
In order to use the overloaded version of std::equal_range() taking the address of the binary comparison predicate as the fourth argument, it is also required to provide conversion operators that enable conversion between the involved types according to
http://en.cppreference.com/w/cpp/algorithm/equal_range
To do this type conversion, within the context of the problem described in the original question (see link at the beginning), conversion operators need to be provided that could convert between V const& and std::pair<K const&, V const&>, where K and V are the template arguments. My understanding is that a conversion operator can only be defined within the scope of a class/template class and the only possibility is to convert from the class/template class to a required type.
In the light of this restriction, is there a way that the conversion operators could be defined to address the prerequisite for invoking the overloaded version of std::equal_range(), as intended within the scope of the discussion presented in the original question (see link at the beginning)?
This code:
bool contains = std::find(indexes.begin(), indexes.end(), i) != indexes.end();
CardAbility* cardAbility = contains ? new CardAbilityBurn(i) : new CardAbilityEmpty;
gives me the following error:
Incompatible operand types CardAbilityBurn and CardAbilityEmpty
However if I write the code like this:
if (contains)
{
cardAbility = new CardAbilityBurn(i);
}
else
{
cardAbility = new CardAbilityEmpty;
}
then the compiler doesn't mind. Why so? I want to use ternary conditional operator because it is just one line. What's wrong there?
I need to note (I think you might need this information) that CardAbilityEmpty and CardAbilityBurn both derive from CardAbility so they are so to say brothers.
Thanks
C++'s type system determines expressions' types from the inside out[1]. That means that the conditional expression's type is determined before the assignment to CardAbility* is made, and the compiler has to choose with only CardAbilityBurn* and CardAbilityEmpty*.
As C++ features multiple inheritance and some more possible conversion paths, since none of the types is a superclass of the other the compilation stops there.
To compile successfully, you need to provide the missing part : cast one or both operands to the base class type, so the conditional expression as a whole can take that type.
auto* cardAbility = contains
? static_cast<CardAbility*>(new CardAbilityBurn(i))
: static_cast<CardAbility*>(new CardAbilityEmpty );
(Note the use of auto, since you already provided the destination type in the right-side expression.)
It is however a bit convoluted, so in the end the if-else structure is better-suited in this case.
[1] There is one exception : overloaded function names have no definitive type until you convert them (implicitly or explicitly) to one of their versions.
There are several cases described for Microsoft compilers, how to handle operand types.
If both operands are of the same type, the result is of that type.
If both operands are of arithmetic or enumeration types, the usual
arithmetic conversions (covered in Arithmetic Conversions) are performed to
convert them to a common type.
If both operands are of pointer types or if one is a pointer type and the
other is a constant expression that evaluates to 0, pointer conversions are
performed to convert them to a common type.
If both operands are of reference types, reference conversions are
performed to convert them to a common type.
If both operands are of type void, the common type is type void.
If both operands are of the same user-defined type, the common type is
that type.
If the operands have different types and at least one of the operands
has user-defined type then the language rules are used to
determine the common type. (See warning below.)
And then there is a caution:
If the types of the second and third operands are not identical, then
complex type conversion rules, as specified in the C++ Standard, are
invoked. These conversions may lead to unexpected behavior including
construction and destruction of temporary objects. For this reason, we
strongly advise you to either (1) avoid using user-defined types as
operands with the conditional operator or (2) if you do use
user-defined types, then explicitly cast each operand to a common
type.
Probably, this is the reason, Apple deactivated this implicit conversion in LLVM.
So, if/else seems to be more appropriate in your case.
All the authors write that, by default, the compiler generates
1. the default constructor
2. copy constructor
3. assignment operator
4. destructor
The other day a friend of mine invited for an interview and there he was told that by default, the compiler generates still one function reference operator (operator &()). it is in fact so?
No, that is not true. If there is no operator& present, then the one in the core language is used.
N3485 13.3.1.2 [over.match.oper]/1-2 (emphasis mine):
If no operand of an operator in an expression has a type that is a class or an enumeration, the operator is assumed to be a built-in operator and interpreted according to Clause 5. [ Note: Because ., .*, and :: cannot be overloaded, these operators are always built-in operators interpreted according to Clause 5. ?: cannot be overloaded, but the rules in this subclause are used to determine the conversions to be applied to the second and third operands when they have class or enumeration type (5.16). —end note ]
If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator. In this case, overload resolution is used to determine which operator function or built-in operator is to be invoked to implement the operator.
You can also see this in that the functions implicitly declared for you are listed as the "Special Member Functions (Clause 12), which makes no reference to operator&.
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.