Variables, Types & Type Inference (auto)

ভ্যারিয়েবল, টাইপ ও auto

Read: ~30 min 10 practice problems

1. C++ Is Statically Typed

Every variable has a type known at compile time. The type determines its size, what operations are allowed, and how the bits are interpreted. Unlike Python, you cannot reassign an int variable to hold a string later.

C++-এ প্রতিটি ভ্যারিয়েবলের একটি নির্দিষ্ট type থাকে — এটি compile-time-এ ঠিক হয়। অর্থাৎ একটি int variable পরে আর string-এ পরিবর্তন করা যাবে না।

2. Built-in Primitive Types

TypeTypical sizeUse for
bool1 bytetrue / false
char1 byteSingle character
short2 bytesSmall integers
int4 bytesGeneral integers (default choice)
long long8 bytesLarge integers
float4 bytesLess precise reals
double8 bytesDefault real (use this)

3. Declaration & Initialization

init.cpp
#include <iostream>

int main() {
    int a = 5;            // copy initialization
    int b(5);            // direct initialization
    int c{5};            // uniform (brace) initialization — C++11+
    int d = {5};          // copy-list initialization

    std::cout << a << " " << b << " " << c << " " << d << "\n";

    // Brace init catches narrowing!
    // int e{3.14};   // ERROR: narrowing conversion

    return 0;
}
Modern recommendation Use brace initialization {} when possible — it catches dangerous narrowing conversions at compile time.

4. auto — Type Inference

Since C++11, you can use auto to let the compiler deduce the type from the initializer. The variable still has a strict static type — auto just saves typing.

auto.cpp
#include <iostream>
#include <vector>
#include <string>

int main() {
    auto i = 42;              // int
    auto d = 3.14;            // double
    auto s = std::string{"hi"}; // std::string
    auto v = std::vector<int>{1,2,3}; // std::vector<int>

    std::cout << i << " " << d << " " << s << " size=" << v.size() << "\n";
}
auto pitfalls auto drops const and reference by default. Use const auto& when you want a non-copying view.

5. const and constexpr

const means: cannot change after initialization. constexpr means: must be a compile-time constant. Use them aggressively — the compiler optimizes harder when it knows things can't change.

const.cpp
#include <iostream>

int main() {
    const int max_users = 100;
    constexpr double PI = 3.14159265358979;

    // max_users = 200; // ERROR: read-only

    std::cout << "Max = " << max_users << ", PI = " << PI << "\n";
}

6. Type Conversion

Implicit conversions can hide bugs. Use explicit casts:

cast.cpp
#include <iostream>

int main() {
    int a = 7, b = 2;
    std::cout << a / b << "\n";                    // 3 (integer division)
    std::cout << static_cast<double>(a) / b << "\n"; // 3.5
}

C++ has four named casts:

  • static_cast<T>(x) — safe compile-time conversion
  • const_cast<T>(x) — strip const (rare)
  • reinterpret_cast<T>(x) — bit-level reinterpretation (dangerous)
  • dynamic_cast<T>(x) — safe downcast in inheritance hierarchies

7. Practice Problems

  1. Declare three variables with brace initialization: an int 7, a double 2.5, a string "C++".
    ✨ Show Answer
    int a{7};
    double d{2.5};
    std::string s{"C++"};
  2. Why does int x{3.14}; fail to compile?
    ✨ Show Answer

    Brace init disallows narrowing conversions. Converting double 3.14 to int loses precision (would store 3). Use int x = 3.14; if you really want truncation, or int x{static_cast<int>(3.14)};.

  3. Use auto to declare a variable holding the result of 1 + 2.5. What's its type?
    ✨ Show Answer

    auto x = 1 + 2.5; — x has type double because 1 is promoted to double before adding.

  4. What's the difference between const int and constexpr int?
    ✨ Show Answer

    const: value can't be modified, but it could be set at runtime. constexpr: value must be known at compile time. Every constexpr is also const, but not vice versa.

  5. Predict the output: std::cout << 7/2 << " " << 7/2.0;
    ✨ Show Answer

    3 3.5. Integer division truncates; mixing int with double promotes to double division.

  6. Why prefer const auto& over auto in a range-for?
    ✨ Show Answer

    auto copies each element. For large objects (strings, vectors), copying is wasteful. const auto& binds a read-only reference — no copy, plus the compiler enforces you don't accidentally modify the element.

  7. Convert double pi = 3.14; to int safely.
    ✨ Show Answer

    int p = static_cast<int>(pi); — explicit, searchable, intentional. Avoid C-style (int)pi.

  8. Write a constexpr function that returns the square of an integer.
    ✨ Show Answer
    constexpr int square(int x) { return x * x; }
    // constexpr int s = square(7); // computed at compile time → 49
  9. What does uint32_t mean?
    ✨ Show Answer

    An unsigned integer with exactly 32 bits, defined in <cstdint>. Range: 0 to 4,294,967,295. Use it when exact size matters (file formats, network protocols).

  10. Demonstrate that bool takes 1 byte despite being conceptually 1 bit.
    ✨ Show Answer
    boolsize.cpp
    #include <iostream>
    int main() {
        std::cout << sizeof(bool) << "\n"; // 1
    }

    Memory is byte-addressable; CPUs can't address single bits. std::vector<bool> is the rare exception that packs bits.

Summary

C++ is statically typed — every variable has a compile-time type. Use brace init {} to catch narrowing. Use auto when the type is obvious from the initializer; use const auto& in loops to avoid copies. const means immutable; constexpr means compile-time-known.

Next Module → Operators, Expressions & Precedence.