Writing static_cast makes a conversion visible, but does not prove that the destination can represent the source value. A quantity can wrap when narrowed, lose a fractional part, or cause undefined behavior if a floating-to-integer conversion exceeds the destination's range. Arithmetic can overflow before the assignment that appears to store it in a larger type.
This guide uses C++17, including integer from_chars. You will check an explicit byte-range quantity, implement a safe nonnegative addition and parse an entire integer field. C++20 adds utilities such as in_range for some integer checks, but the examples do not require them. The safety policy is stated before the casts are performed.
Range and precision require independent checks
A destination's range determines which magnitudes it can represent. Precision determines whether the exact value survives. A large integer can fit within a floating-point type's magnitude range while losing neighboring integer distinctions. A floating-point fraction converted to an integer truncates toward zero rather than rounding.
Choose the representation from the requirement. Identifiers and counts generally need exact integral values. Measurements may use floating point under a specified tolerance. Monetary values need a defined representation and rounding policy. A cast should implement that decision, not conceal it. List initialization can reject some narrowing conversions at compile time, but runtime input still needs explicit checks when the compiler cannot prove the range.
Signed overflow must be prevented before it occurs
For signed integers, exceeding the representable range during ordinary arithmetic has undefined behavior. Testing the result after an overflowing addition is too late. Check the operands against a safe boundary first, or use an appropriate verified arithmetic abstraction. The main example restricts both operands to nonnegative values, allowing a simple check against maximum minus the second operand.
Assigning an int multiplication to a long long does not automatically widen the multiplication itself. Convert an operand before multiplying when that provides a proven adequate range. Unsigned arithmetic follows modulo rules, but wrapping can still violate a business requirement. Do not choose unsigned merely to make overflow “defined” if the operation must reject excessive values. Keep the domain's permitted maximum separate from the language type's maximum.
Named casts describe different operations
Static_cast handles many explicit conversions but is not a general runtime range checker. Dynamic_cast can check a suitable polymorphic downcast: a failed pointer cast returns null, while a failed reference cast throws. Const_cast changes selected qualification, but does not permit writing to an object originally declared const.
Reinterpret_cast expresses low-level representation-related conversions and does not make arbitrary aliasing or object-lifetime assumptions valid. Avoid using it to reinterpret unrelated objects as though their representations were interchangeable. If a binary format needs decoding, read its defined bytes into the intended values under explicit width and byte-order rules. C++20 bit_cast serves a different carefully constrained representation operation, and still does not convert file formats automatically.
Signed and unsigned mixing can change comparisons
Container sizes commonly use an unsigned size type. Comparing a negative signed index with such a size can convert the negative value to a large unsigned value. The result can be surprising even when both original values are easy to understand. Validate negativity first, then compare under a deliberate compatible representation.
Do not silence the warning with an arbitrary cast before checking. Casting a negative input to size_t preserves none of the user's intended meaning as an index. A checked index wrapper or an explicit validation boundary can make the policy reusable. For loops, choose an index type matching the operation or use a range-based loop when the index is unnecessary. Compiler conversion warnings are useful prompts to review the actual range argument.
Test the boundary argument explicitly
Test the smallest and largest accepted values, one value outside each domain boundary, a representation overflow, negative input and partial text. For numeric conversion, also test precision-sensitive values and fractional input if they can arrive. Document whether truncation, rounding or rejection is required.
Avoid executing an undefined operation merely to “see what happens.” A particular output is not evidence that undefined behavior is safe. Use compiler warnings and sanitizers as additional diagnostics while keeping the mathematical range proof in the implementation. Conversion code should be small enough that a reviewer can state why the cast is permitted for every accepted input.
Official references
C++ Casts and Numeric Safety Examples
Each example shows the code, the output it produces and what each part does.
Reject narrowing and excessive addition
Compile this complete program as C++17. The byte conversion occurs only after proving the requested range. The addition helper validates its limited nonnegative contract before computing the result.
Exact-width uint8_t is optional on implementations without a suitable eight-bit unsigned type; ordinary modern desktop targets provide it. Casting the accepted byte to int for output avoids character-like stream insertion.
#include <cstdint>
#include <iostream>
#include <limits>
#include <stdexcept>
std::uint8_t byte_quantity(int value) {
if (value < 0 || value > 255)
throw std::out_of_range("byte quantity");
return static_cast<std::uint8_t>(value);
}
int add_nonnegative(int left, int right) {
if (left < 0 || right < 0)
throw std::invalid_argument("negative operand");
if (left > std::numeric_limits<int>::max() - right)
throw std::overflow_error("addition overflow");
return left + right;
}
int main() {
std::cout << static_cast<int>(byte_quantity(200)) << '\n';
try {
(void)byte_quantity(300);
} catch (const std::out_of_range&) {
std::cout << "Quantity rejected\n";
}
try {
(void)add_nonnegative(std::numeric_limits<int>::max(), 1);
} catch (const std::overflow_error&) {
std::cout << "Addition rejected\n";
}
}200
Quantity rejected
Addition rejectedC++ Casts and Numeric Safety in Real Projects
Worked exercise: parse the complete quantity field
The task is to accept an ASCII integer from zero through one thousand, with no surrounding whitespace or trailing text. Integer from_chars avoids culture-sensitive parsing and reports both a status and the stopping position. The field is valid only when the entire text is consumed and the domain range succeeds.
#include <charconv>
#include <iostream>
#include <string_view>
#include <system_error>
bool parse_quantity(std::string_view text, int& value) {
if (text.empty() || text.size() > 10)
return false;
int candidate = 0;
auto result = std::from_chars(
text.data(), text.data() + text.size(), candidate);
if (result.ec != std::errc{} ||
result.ptr != text.data() + text.size() ||
candidate < 0 || candidate > 1000)
return false;
value = candidate;
return true;
}
int main() {
for (std::string_view input : {"12", "12x", "-1", "1001"}) {
int quantity = 0;
if (parse_quantity(input, quantity))
std::cout << "Accepted: " << quantity << '\n';
else
std::cout << "Invalid quantity\n";
}
}Accepted: 12
Invalid quantity
Invalid quantity
Invalid quantityA partial parse of 12x is rejected because the pointer does not reach the end. The candidate is assigned to the output only after every check succeeds, so failure does not leak a partly validated value.
C++ Casts and Numeric Safety FAQ
Does static_cast check that a number fits?
Generally no. It expresses an explicit conversion under the language's rules, some of which discard information or require a representable result. Validate the destination's range and the domain's rules first. Use the cast only after the argument is established. A visible cast is better than hidden intent, but visibility alone does not make the operation safe.
Why not check for signed overflow after adding?
The overflowing signed operation already has undefined behavior, so reasoning from its result is invalid. Check operands before computing or use a verified arithmetic mechanism. A nonnegative addition can compare the first operand with maximum minus the second. More general arithmetic needs corresponding sign-aware checks. Avoid assumptions based on the behavior of one compiler's unoptimized build.
Is unsigned wrap acceptable because it is defined?
Only if modulo arithmetic is the actual requirement. A quantity, allocation size or financial total usually must reject overflow rather than wrap to a small value. Defined language behavior can still be a serious application bug. Keep domain limits explicit and verify operations before using their results for allocation, indexing or persistent state changes.
Why check both from_chars status and its pointer?
The status reports conversion success or failure, while the pointer identifies where parsing stopped. A valid initial integer followed by extra text can produce a successful partial parse. Requiring the pointer to reach the end enforces the whole-field contract. Add domain validation afterward, because a fully parsed negative or excessively large quantity can still be disallowed.
C++ Casts and Numeric Safety Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
from_chars parses 12 from 12x without a conversion error. What additional check rejects this field?
C++ Casts and Numeric Safety Quiz
2 questions to check what you learned.
What does static_cast by itself establish for a narrowing numeric conversion?
- That every source value fits.
- That fractional input is rounded to nearest.
- That the conversion is explicitly requested under the language rules.
- That arithmetic before the cast cannot overflow.
Which signed-overflow strategy is sound?
- Replace every signed type with unsigned without reviewing the requirement.
- Add first and inspect whether the result became negative.
- Assume debug and release builds wrap identically.
- Prove the operation fits before performing it.