Question

How to Get and Print a Variable Type in Standard C++

cppvariablesc++11typeof

Question

In standard C++, is it possible to print the type of a variable by name?

For example:

int a = 12;
std::cout << typeof(a) << std::endl;

Expected output:

int

What is the standard C++ way to do this, and are there any limitations?

Short Answer

By the end of this page, you will understand how C++ handles type information at compile time and runtime, why there is no standard typeof operator that directly prints int, and how tools like typeid, type traits, and compiler-specific name demangling are commonly used instead.

Concept

C++ does not provide a standard feature that guarantees you can write code like this:

std::cout << typeof(a);

and get a clean human-readable type name such as int.

The core idea is that types in C++ are mainly a compile-time concept. The compiler uses type information to check correctness, choose overloads, generate code, and optimize programs. But once the program runs, that exact source-level type name is not always preserved in a portable, readable form.

What standard C++ does provide

Standard C++ provides runtime type information through typeid:

#include <iostream>
#include <typeinfo>

int main() {
    int a = 12;
    std::cout << typeid(a).name() << '\n';
}

This gives you a type name string, but there is an important limitation:

  • typeid(...).name() is implementation-defined
  • That means the output depends on the compiler
  • It may print int, or it may print something cryptic like i

So typeid is standard, but the exact printed name is not standardized.

Why this matters

Understanding this helps you avoid expecting reflection-like behavior from C++. In many languages, type names are easy to inspect at runtime. In C++, this is more limited because:

  • performance and compile-time code generation are important
  • type information is not designed primarily for runtime display
  • readable type names are often compiler-specific

Compile-time vs runtime

There are two related but different problems:

  1. Knowing a type in code

    • Example: checking whether a type is int
    • Usually done with templates or type traits
  2. Printing a type name for debugging or logging

    • Example: showing int on screen
    • Often done with typeid(...).name(), but portability is limited

If your goal is program logic, prefer type traits. If your goal is debugging, typeid may help, but the output may differ by compiler.

Mental Model

Think of a C++ type like a label on a package while it is being sorted in a warehouse.

  • During sorting, the label is extremely important
  • Workers use it to send the package to the right place
  • But after shipping, the warehouse system may not keep the original label in a neat, human-friendly form

In the same way, the C++ compiler depends heavily on type information while building your program. But at runtime, you are not guaranteed to get the exact source-code name back as a clean string.

So:

  • compiler stage = types are rich and important
  • runtime stage = some type info may exist, but readable names are limited
Take Quiz

Syntax and Examples

Standard approach with typeid

#include <iostream>
#include <typeinfo>

int main() {
    int a = 12;
    std::cout << typeid(a).name() << '\n';
}

What this does

  • typeid(a) returns a std::type_info object describing the type
  • .name() returns a C-style string representing that type
  • The exact string is compiler-dependent

Example with multiple types

#include <iostream>
#include <string>
#include <typeinfo>

int main() {
    int a = 12;
    double b = 3.14;
    std::string c = "hello";

    std::cout << typeid(a).name() << '\n';
    std::cout << typeid(b).name() << '\n';
    std::cout << typeid(c).name() << '\n';
}

Depending on your compiler, the output might be readable or abbreviated.

Comparing types instead of printing them

If you want to check a type rather than print it, use type comparisons:

#include <iostream>
#include <typeinfo>

int main() {
    int a = 12;

    if (typeid(a) == typeid(int)) {
        std::cout << "a is an int\n";
    }
}

Better for compile-time logic: type traits

#include <iostream>
#include <type_traits>

int main() {
    int a = 12;

    if constexpr (std::is_same_v<decltype(a), int>) {
        std::cout << "a has type int\n";
    }
}

This is often the better solution when your goal is program behavior, not printing.

Important note about typeof

// Not standard C++
// typeof(a)

Some compilers historically provided typeof as an extension, but it is not part of standard C++.

The standard keyword that gets a type from an expression is:

decltype(a)

However, decltype(a) gives you a type in code, not a printable string.

Step by Step Execution

Consider this example:

#include <iostream>
#include <typeinfo>

int main() {
    int a = 12;
    std::cout << typeid(a).name() << '\n';
}

Step-by-step

1. int a = 12;

A variable named a is created with type int and value 12.

2. typeid(a)

The expression typeid(a) asks C++ for type information about a.

This produces a std::type_info object representing the type.

3. .name()

Calling .name() on that object returns a string-like C-style name for the type.

Important:

  • the returned text is implementation-defined
  • it might be int
  • it might be a shorter encoded form such as i

4. std::cout << ...

The returned name is printed to standard output.

Another traceable example

#include <iostream>
#include <typeinfo>

int main() {
    int x = 5;
    double y = 2.5;

    std::cout << typeid(x).name() << '\n';
    std::cout << typeid(y).name() << '\n';
}

Execution flow:

  1. Create x as int
  2. Create y as double
  3. Ask for type info of x
  4. Print its compiler-provided name
  5. Ask for type info of y
  6. Print its compiler-provided name

Real World Use Cases

Debugging template code

When working with templates, developers often want to inspect what type the compiler deduced:

template <typename T>
void printType(const T& value) {
    std::cout << typeid(value).name() << '\n';
}

This can help during development, especially when generic code becomes hard to follow.

Logging unexpected values

In serialization, parsing, or generic utility code, you may log the type of a value when debugging behavior.

Verifying polymorphic behavior

With base-class references or pointers, typeid can help inspect the dynamic type when runtime type information is enabled and polymorphism is involved.

Unit testing generic utilities

Developers sometimes check that a helper produces the expected type using decltype and type traits instead of printing names.

static_assert(std::is_same_v<decltype(1 + 2), int>);

This is more reliable than printing type names.

Take Quiz

Real Codebase Usage

In real C++ projects, developers usually do not rely on printing raw type names for core logic.

Common patterns

Guarding behavior with type traits

#include <type_traits>

template <typename T>
void process(const T& value) {
    if constexpr (std::is_integral_v<T>) {
        // handle integers
    } else {
        // handle other types
    }
}

This is common in template-heavy code.

Using decltype for deduced types

auto x = 10;
decltype(x) y = 20;

Developers use this to keep types consistent without manually repeating them.

Debug-only type inspection

std::cerr << "Type: " << typeid(value).name() << '\n';

This is often used temporarily while debugging.

Validation and static checks

static_assert(std::is_same_v<decltype(std::declval<int>() + std::declval<int>()), int>);

This confirms type behavior at compile time.

Why this is preferred

  • type traits are portable
  • compile-time checks are safer
  • logic based on type names as strings is fragile
  • readable type printing often depends on compiler tools outside the standard

Common Mistakes

Mistake 1: Expecting typeof to be standard C++

int a = 12;
std::cout << typeof(a) << '\n';

This is not standard C++.

Fix

Use decltype for type deduction in code, or typeid for runtime inspection.

std::cout << typeid(a).name() << '\n';

Mistake 2: Assuming typeid(...).name() always prints int

int a = 12;
std::cout << typeid(a).name() << '\n';

You might expect:

int

But some compilers may print encoded names.

Fix

Treat .name() as useful for debugging, not as a portable user-facing string.

Mistake 3: Using printed type names in program logic

if (std::string(typeid(a).name()) == "int") {
    // fragile
}

This is unreliable because the name is implementation-defined.

Fix

Use proper type comparisons:

if (typeid(a) == typeid(int)) {
    // reliable comparison
}

Or use type traits:

if constexpr (std::is_same_v<decltype(a), int>) {
    // compile-time check
}

Mistake 4: Confusing decltype with a printable value

std::cout << decltype(a) << '\n';

This is invalid because decltype(a) is a type, not a runtime value.

Fix

Use it in declarations or type traits:

decltype(a) b = 20;

Mistake 5: Forgetting the correct headers

  • typeid needs #include <typeinfo>
  • type traits need #include <type_traits>

Comparisons

FeatureStandard C++PurposeOutput readabilityBest use
typeid(expr).name()YesRuntime type inspectionImplementation-definedDebugging
typeid(expr) == typeid(T)YesRuntime type comparisonNot applicableChecking types at runtime
decltype(expr)YesGet the type of an expression in codeNot printable directlyDeclarations, templates
std::is_same_v<T, U>YesCompare types at compile timeNot printable directlyTemplate logic
typeof(expr)NoCompiler extension in some environmentsVariesAvoid in portable code

typeid vs decltype

typeiddecltype
Works at runtimeWorks at compile time
Gives type information objectGives a type in code
Can be used with .name()Cannot be printed directly
Useful for inspectionUseful for declarations and metaprogramming

Which one should you use?

  • Use typeid when debugging or inspecting type information at runtime
  • Use decltype when you need to refer to a deduced type in code
  • Use type traits when program behavior depends on a type
Take Quiz

Cheat Sheet

Quick reference

Print a type name candidate

#include <typeinfo>
std::cout << typeid(x).name() << '\n';
  • Standard C++: yes
  • Exact string output: not portable

Compare a variable's type

if (typeid(x) == typeid(int)) {
    // x is int
}

Get a type in code

decltype(x) y = x;
  • decltype(x) is a type, not a string

Compile-time type check

#include <type_traits>

if constexpr (std::is_same_v<decltype(x), int>) {
    // x is int
}

Rules to remember

  • typeof is not standard C++
  • typeid(...).name() is standard, but the returned text is implementation-defined
  • Use decltype for type deduction in declarations
  • Use std::is_same or other type traits for reliable type-based logic
  • Do not compare .name() strings for portable code

Common headers

#include <typeinfo>     // typeid
#include <type_traits>  // std::is_same_v, std::is_integral_v

FAQ

Can I print int exactly in standard C++?

Not portably. typeid(x).name() is standard, but the exact text it returns is implementation-defined.

Is typeof valid in C++?

Not in standard C++. Some compilers support it as an extension, but portable C++ should not rely on it.

What should I use instead of typeof in standard C++?

Use decltype to refer to a type in code, and typeid if you need runtime type information.

Why does typeid(a).name() print i instead of int?

Because the format of the returned name depends on the compiler implementation. Some compilers return mangled or abbreviated names.

How do I check whether a variable is an int?

Use typeid(a) == typeid(int) at runtime, or std::is_same_v<decltype(a), int> at compile time.

Is decltype(a) the same as typeid(a)?

No. decltype(a) gives you a type in code at compile time. typeid(a) gives type information at runtime.

Should I use printed type names in application logic?

No. Use type comparisons or type traits instead. Printed names are not portable enough for logic.

Take Quiz

Mini Project

Description

Build a small C++ program that inspects values of different types and shows how standard C++ can compare types safely, while also demonstrating the limits of printing human-readable type names. This helps reinforce the difference between runtime inspection and compile-time type checking.

Goal

Create a program that prints compiler-provided type names and reports whether each variable is an int using a safe standard approach.

Requirements

  • Create variables of at least three different types
  • Print each variable's type using typeid(...).name()
  • Check whether each variable is an int
  • Use standard C++ only
  • Keep the output easy to read
Take Quiz

Keep learning

Related questions

View more