Concepts / Explicit and Implicit Type Conversions

Explicit and Implicit Type Conversions

The type system can tell that a variable is an integer, but it reasons about the set of values that variable could hold rather than the single value it holds at a particular moment.

  • Programming

A Cast That Seems to Check Itself

Many programmers write a cast when a value does not match the type they need, and they assume the cast quietly makes sure the result is valid. The reasoning feels sound: a cast is a deliberate act, so the language should be guarding the result. This article starts from that belief, tests it, and builds a more accurate picture of what a type conversion promises and what it leaves to you.

What do you think happens?

When you write a static_cast to convert a value to a smaller type, does the language first check that the value fits?

  • Yes, the cast verifies the value before converting it

  • No, the cast converts the value without checking whether the conversion is safe

Reveal answer
Answer:

No, the cast converts the value without checking whether the conversion is safe

A static_cast converts a value without checking it, so correctness depends on the programmer's judgment. The cast trusts you, and it will not stop to ask whether the particular value at hand is safe to convert.

What the Type System Actually Knows

Definition

The type system is the part of the language that assigns each value a type and decides which operations make sense on it. Its knowledge is built from the declarations in your program.

The type system can tell that a variable is an integer, but it reasons about the set of values that variable could hold rather than the single value it holds at a particular moment. The source puts it bluntly: the type system has no common sense, and it knows about the possible values of variables rather than their actual values.

This gap explains a behavior that can look like an overreaction. Because the type system reasons about everything a variable might hold, it may issue a warning for code that is perfectly safe for the values that actually occur. From the type system's point of view the warning is not a false alarm, because it only knows the possibilities. It is a limitation of what the system can know before the program runs.

Declared typeintVariable at runtimeone stored valuePossible valuesevery value an int couldholdActual valuethe single value thatoccursWarning reasoningmust cover allpossibilitiesdefinesdrivesholds
What does the type system know about a variable that is declared as an int, compared with the one value the program holds at a particular moment?

The contrast with the type system is useful. The type system is cautious because it must consider every possible value. A static_cast is confident because it has been told to trust you. The cast can therefore accept a conversion that the type system would have questioned, and it will not stop to ask whether the particular value at hand is safe.

Tracing a Value Through a Cast

The source gives a concrete situation: converting the size of an initializer list to an int. An initializer list is a sequence of values written between braces to build a container. Its size is a count of how many elements it holds. The library reports that count with a type that is often larger than int, so storing it in an int requires a conversion, and a static_cast is one way to write it.

Initializer listvalues between bracesElement counthow many elements it holdsCount typeoften larger than intstatic_cast to intconverts without a checkint destinationassumed to hold the countcountedreported ascaststored
How does the element count move from the initializer list through the static_cast into the int destination, and where does the assumption that it fits enter?
Worked example

Converting a small initializer list size

A program builds a container from an initializer list of five values, then stores the element count in an int using a static_cast. Trace what the conversion does and what it assumes.

  1. 1
    Identify the source value

    The initializer list holds five values, so its size is the count five, reported in a type that is often larger than int.

  2. 2
    Apply the static_cast

    The static_cast converts that count to int. The cast does not look at the list or ask whether the count is small enough for int; it performs the conversion as instructed.

  3. 3
    Name the assumption

    The cast assumes the count fits in an int. For this list, five fits, so the assumption holds for the value that actually occurs.

  4. 4
    Ask what would change

    The same cast written against a list whose count exceeded the int range would still compile and still convert without any check. Nothing in the cast itself would warn you that the assumption had failed.

Result

The static_cast stores the count five in the int. It works because the programmer assumed the count fits, and the cast itself never verified that assumption.

Explicit Versus Implicit Conversion

Explicit type conversions are often called casts. A cast is a way of overriding what the type system concluded, which is why it is most useful when the type system's objection is either wrong or cannot be resolved any other way. Converting without writing a cast is an implicit conversion, where the language performs the conversion on your behalf. The difference lies in who decides to convert and whether the programmer has signaled that the conversion was intended.

AspectImplicit conversionExplicit conversion (static_cast)
Who starts the conversionThe language, without a cast written in the codeThe programmer, by writing the cast
Has intent been signaledNot by a cast in the sourceYes, the cast is a deliberate act
Is the value checkedThe type system reasons about possible valuesNo, the cast converts without checking whether it is safe
Typical roleRoutine conversion between compatible typesOverriding a conclusion the type system reached

Who decides to convert, and what each form promises

Deciding Between a Cast and a Check

Because a static_cast trusts the programmer, the source recommends a simple habit: when in doubt, check the value before converting it instead of relying on the cast to be safe. The check does not need to be elaborate. Its purpose is to make the assumption visible and to confirm it for the value you actually have.

Value to convertfor example an initializerlist sizeCast is justifiedassumption is confirmedConvert withstatic_castKnown to be safe?Check the valueconfirm before convertingValue passes check?Handle the casedifferentlyyesno or in doubtyesno
Which path should the code take depending on whether the value is known to be safe, and where does the check fit before the cast?

Mistakes When Casting Sizes

  • Common mistake

    Assuming the cast verifies the value

    What it looks like

    Writing a static_cast to store a count in an int and assuming a too-large count would be caught.

    Why it fails

    A static_cast converts a value without checking it, so the language does not confirm that the count fits.

    Do this instead

    Confirm the assumption yourself, or check the value before converting it.

  • Common mistake

    Treating a type-system warning as a false alarm about your values

    What it looks like

    Ignoring a warning because the current list happens to be small.

    Why it fails

    The type system reasons about every possible value, so a warning can concern values that are safe for this run but not in general.

    Do this instead

    Read the warning as a statement about possibilities, then decide whether a check or a better type choice resolves it.

  • Common mistake

    Reaching for a cast to silence the compiler

    What it looks like

    Adding a cast whenever a conversion is flagged, without asking why the types disagree.

    Why it fails

    Explicit conversions prop up something broken, so a cast hides the mismatch rather than fixing it.

    Do this instead

    Revisit the type choice or the library operation so the conversion is not needed.

Practice: Justified Cast or Checked Value

MEDIUM

A program stores the element count of an initializer list in an int using a static_cast. Describe the assumption the cast carries. Then say whether you would cast directly or check the value first, and explain what the check would confirm.

Hints
  • Think about what the static_cast does not do before it converts.
  • Consider whether the value that occurs at run time is the same as the set of values the type system considers possible.
  • Ask whether the cast is overriding a conclusion or merely expressing a confirmed fact.

Key Takeaways

  • A static_cast converts a value without checking it, so correctness depends on the programmer's judgment.
  • The type system reasons about the possible values of a variable, not the actual value it holds at run time, which is why it can warn about code that is safe for the values that occur.
  • Converting an initializer list size to an int with a static_cast carries the assumption that the count fits in an int, and the cast does not verify that assumption.
  • When in doubt, check the value before converting it, and prefer careful type choices and well-designed libraries over casts that prop up something broken.

Related concepts

More concepts starting with E