Skip to content

Interview Questions Answer

  • 重载iostream
  • 重载 format

C++11 Standard

  • Explain auto type deduction rules. What cases can’t be deduced / are common pitfalls?

  • all the template deduction rules apply.

  • By-value auto drops references and top-level const

  • auto& keeps lvalue references; can’t bind to rvalues

  • auto&& is a forwarding reference (universal reference)

  • Arrays/functions decay to pointers with plain auto

  • Braced initialization {} is the biggest C++11 pitfall

    • auto with braces deduces std::initializer_list if possible

    • What auto cannot deduce in C++11

    • Function return type from body (C++11 requires trailing return type)

    • Function parameters (not allowed until C++14 generic lambdas / C++20)
    • Non-static class members need an initializer

    • Proxy types,

    std::vector<bool> a = {true};
    auto b = a[0];  // type = std::vector<bool>::reference  因为C++的 vector<bool> 压缩了bit
    bool* p = &b; // error
    
  • Explain the difference between auto and decltype. When would you use each?

  • What are the main benefits and pitfalls of uniform initialization {} in C++11?

  • One syntax for many cases

  • Prevents narrowing conversions (safer)
  • Avoids some parsing ambiguities (“most vexing parse”)
  • pitfalls:

    • {} prefers std::initializer_list overloads
    • Empty braces can pick the “wrong” overload A a3{}; // might call initializer_list ctor (empty list)
    • auto + {} gives initializer_list
  • How does nullptr differ from NULL and 0? Show an overload example where it matters.

  • What is enum class and why is it safer than a traditional enum?

  • Scoped names (no global pollution)

    enum Color { Red, Green };
    enum Traffic { Red, Yellow };  // ERROR: Red already defined
    enum class Color { Red, Green };
    enum class Traffic { Red, Yellow }; // OK
    
  • enum class does not implicitly convert

    int x = static_cast<int>(Color::Green);
    
  • What does =delete do? How is it different from making a function private?

  • =delete explicitly disables a function.

  • What does =default mean? When is it useful?

  • =default tells the compiler to generate the default implementation of a special member function.

  • 比如写了析构函数后,编译器不会再自动生成 move 构造/赋值; 你可以显式 =default 回来:

  • What is override? What bug does it prevent?

  • It prevents accidental non-overriding due to a signature mismatch.

    struct Base {
        virtual void draw() const;
    };
    struct Derived : Base {
        void draw();   // OOPS: missing `const` -> does NOT override!
    };
    
  • Explain the “rule of five” in C++11.

  • What problem do rvalue references solve? Why wasn’t copy elision enough?

  • Avoid expensive deep copies of temporaries

    std::string make();
    std::string s = make();  // pre-C++11: copy (unless elided)
    std::string s = make();  // moves (steals buffer) if not elided
    
  • Enable move semantics in containers v.push_back(Big{});

  • Express ownership transfer in APIs

  • Make generic code efficient (perfect forwarding)

    template<class T, class... Args>
    std::unique_ptr<T> makeunique(Args&&... args) {
        return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
    }
    
  • What does std::move actually do? Does it move anything by itself?

  • What it actually does is cast its argument to an rvalue (an “expiring value”)

  • If the type has no move constructor, the compiler falls back to copying

  • When will the compiler not generate a move constructor / move assignment operator?

  • Move constructor is not declared if you declare any of these yourself:

    • copy constructor
    • copy assignment operator
    • move constructor
    • move assignment operator
    • destructor
  • Write a typical move constructor and move assignment for a resource-owning class. What are the key rules?

  • Key rules / talking points

    1. Steal, don’t copy. Move ctor/assign should transfer ownership of the resource (pointers/handles) instead of duplicating it.
    2. Leave the source in a valid state. After moving, other must be safe to destroy or assign to. Typical pattern: set pointers to nullptr, sizes to 0.
    3. Avoid resource leaks in move assignment. Release your current resource first (delete[] data_) before stealing the new one.
    4. Protect against self-move. if (this != &other) prevents deleting your own resource and then reading it.
    5. Mark moves noexcept when possible. Standard containers (e.g., std::vector) prefer move only if it’s noexcept; otherwise they may fall back to copy.
    6. Rule of five. If you manually manage resources, you usually need: destructor, copy ctor, copy assign, move ctor, move assign.
    7. Keep invariants consistent. After a move, both objects should satisfy class invariants (e.g., size_ == 0 ↔ data_ == nullptr).
  • ``` cpp #include #include

    class Buffer { public: Buffer() : data_(nullptr), size_(0) {}

    explicit Buffer(std::size_t n)
        : data_(n ? new int[n] : nullptr), size_(n) {}
    
    ~Buffer() {
        delete[] data_;
    }
    
    // copy ctor
    Buffer(const Buffer& other)
        : data_(other.size_ ? new int[other.size_] : nullptr),
          size_(other.size_) {
        for (std::size_t i = 0; i < size_; ++i) data_[i] = other.data_[i];
    }
    
    // copy assignment (copy-and-swap for strong exception safety)
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {
            Buffer tmp(other);
            swap(tmp);
        }
        return *this;
    }
    
    // ---- move operations ----
    
    // move ctor
    Buffer(Buffer&& other) noexcept
        : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
    }
    
    // move assignment
    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            delete[] data_;          // release current resource
            data_ = other.data_;     // steal resource
            size_ = other.size_;
            other.data_ = nullptr;   // leave source valid
            other.size_ = 0;
        }
        return *this;
    }
    
    void swap(Buffer& other) noexcept {
        std::swap(data_, other.data_);
        std::swap(size_, other.size_);
    }
    

    private: int* data_; std::size_t size_; };

    ```

  • Why is noexcept important for move operations in standard containers like std::vector?

  • Explain reference collapsing rules. Give examples with T&, T&&, auto&&.

  • ``` cpp template void h(T&& x) { } // forwarding reference

    int i = 0; h(i); // ✅ 接左值,T 推成 int&,x 是 int& h(42); // ✅ 接右值,T 推成 int, x 是 int&& ```

  • What is a “forwarding reference” (universal reference)? When is T&& not an rvalue reference?

  • Explain perfect forwarding. Why do we need std::forward?

  • Compare unique_ptr, shared_ptr, and weak_ptr. When would you use each?

  • Why can’t unique_ptr be copied? How do you transfer ownership?

  • What is a circular reference in shared_ptr and how does weak_ptr fix it?

  • Is shared_ptr thread-safe? What exactly is thread-safe about it?

  • Reference count updates are atomic

  • The managed object itself, is not thread-safe

  • Why should you prefer make_unique / make_shared over calling new directly?

  • Explain lambda capture modes ([], [=], [&], [this]). What lifetime bug can occur?

  • What does mutable mean on a lambda?

  • this modifies the lambda’s internal copy, not the original variable.

  • ``` cpp int x = 0; auto f = x mutable { x++; // OK: modifies the captured copy return x; };

    std::cout << f() << "\n"; // 1 std::cout << f() << "\n"; // 2 std::cout << x << "\n"; // still 0 (original unchanged) ```

  • How would you store a capturing lambda in a variable?

  • Option A: auto (best when type stays local)

  • Option B: std::function (type erasure, flexible)
  • Option C: Template parameter (best for APIs)

  • What is the type of a lambda? Can you name it?

  • Each lambda expression has a unique, unnamed closure type generated by the compiler.You cannot write the type explicitly

  • What’s the difference between push_back and emplace_back?

  • Compare std::map and std::unordered_map: complexity, ordering, and use cases.

  • Explain iterator invalidation rules for vector, deque, list, and map.

  • Any operation that causes reallocation invalidates all iterators, references, and pointers to elements.

  • vector: reallocation ⇒ all invalid; erase/insert shifts ⇒ invalidates at/after position.

    deque: middle insert/erase ⇒ all invalid; end pushes/pops may invalidate (safe to assume yes if growth happens).

    list: only erased elements’ iterators invalidated; everything else stable.

    map: only erased elements’ iterators invalidated; inserts don’t invalidate.

  • What is the difference between std::begin/end and member begin/end? Why does it matter?

  • std::begin/end

    Works for:

    1. standard containers (by calling members),
    2. raw arrays (via overload),
    3. user-defined types that provide begin/end via ADL.
  • cpp #include <iterator> std::vector<int> v{1,2,3}; auto it1 = std::begin(v); // calls v.begin() auto ed1 = std::end(v); // calls v.end() int a[3] = {1,2,3}; auto it2 = std::begin(a); // returns pointer to a[0] auto ed2 = std::end(a); // returns pointer past last

  • When would you choose std::array over std::vector?

  • The size is known and constant

  • You want no heap allocation
  • You need predictable layout / ABI

  • Explain std::tuple and std::tie. Give a practical example.

int id;
std::string name;
bool ok;
std::tie(id, name, ok) = parse_user("raw input");
return std::tie(x, y) < std::tie(other.x, other.y);
struct Point {
    int x, y;
    bool operator<(const Point& other) const {
        return std::tie(x, y) < std::tie(other.x, other.y);
        // lexicographical compare by (x,y)
    }
};
  • Describe how std::thread works. What happens if a std::thread is destroyed without join/detach?

If a std::thread object is still joinable when it’s destroyed, the program calls std::terminate().

  • join() vs detach() — when is each appropriate?

  • What is std::async? What are the launch policies and default behavior?

  • ``` cpp #include

    int work(int x) { return x * 2; }

    int main() { std::future fut = std::async(work, 21); int result = fut.get(); // blocks until work(21) finishes } ```

  • Launch policies:

    • std::launch::async The callable must run asynchronously (usually in a new thread).
    • std::launch::deferredThe callable is not executed immediately.It runs only when you call future.get() or future.wait(), and it runs in that same thread.
    • default behavior.auto fut = std::async(std::launch::async | std::launch::deferred, work, 42); You cannot rely on which one is chosen; it’s implementation-defined.
  • Compare std::lock_guard and std::unique_lock.

  • std::lock_guard is A minimal RAII lock wrapper:

  • unique A more flexible RAII lock:

    • Can be constructed with different lock strategies: std::defer_lock don’t lock yet, std::try_to_lock , std::adopt_lock mutex already locked
    • Supports manual lock/unlock
    • Required by std::condition_variable
    • movable
  • What’s a data race? Give an example and explain how to fix it.

  • A data race occurs when multiple threads access the same memory location concurrently, at least one access is a write, and there’s no proper synchronization.

  • Why is RAII fundamental in C++? How does it relate to exception safety?

  • Tie the lifetime of a resource (memory, file, mutex, socket, etc.) to the lifetime of an object

  • When an exception is thrown, C++ performs stack unwinding:

    • It starts exiting scopes
    • For each scope, it destroys all local objects
    • Each destructor runs and releases its resource

    If resources are managed by RAII objects, exceptions automatically trigger cleanup.

  • What are the major exception safety guarantees (basic/strong/nothrow)?

  • No guarantee: If an operation throws, anything can happen:

  • Basic exception guarantee: no resources are leaked and all objects remain in a valid state
  • Strong: If an exception is thrown, the program state is unchanged — as if the operation never happened.
  • Nothrow guarantee: The operation is guaranteed not to throw exceptions.

  • Why is multiple inheritance tricky? How does virtual inheritance help?

  • What is type erasure? Give an example (std::function, any, etc.).

  • hide the concrete type behind a uniform interface

    • ``` cpp #include #include #include int main() { std::any a = 42; // holds int a = std::string("hi"); // now holds std::string

      try { std::string s = std::any_cast(a); std::cout << s << "\n"; } catch (const std::bad_any_cast&) { // wrong type } } ```

    • ``` cpp #include #include

    int add(int x, int y) { return x + y; }

    int main() { std::function f;

      f = add;                     // function pointer
      std::cout << f(1, 2) << "\n";
    
      f = [](int x, int y){ return x * y; };  // lambda
      std::cout << f(3, 4) << "\n";
    
      struct Adder {
          int operator()(int x, int y) const { return x - y; }
      };
    
      f = Adder{};                 // function object
      std::cout << f(10, 3) << "\n";
    

    } ```

  • Runtime polymorphism without inheritance/virtual base class

  • Implement a small RAII class that owns a file descriptor / handle. Support move, forbid copy.

  • Write a thread-safe singleton in C++11 and explain why it’s safe.

  • Since C++11, initialization of a function-local static variable is guaranteed to be thread-safe.

  • Given a vector of structs, sort by a field using a lambda, then remove duplicates.

  • Implement ScopeGuard: execute a callback automatically on scope exit.

  • ``` cpp #include

    class ScopeGuard { public: // Construct from any callable (lambda, functor, function pointer...) template explicit ScopeGuard(F&& f) : func_(std::forward(f)), active_(true) {}

    // Non-copyable (避免两个 guard 管同一个回调)
    ScopeGuard(const ScopeGuard&) = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;
    
    // Movable
    ScopeGuard(ScopeGuard&& other) noexcept
        : func_(std::move(other.func_)), active_(other.active_) {
        other.dismiss();  // 移动后旧的 guard 不再执行
    }
    
    ScopeGuard& operator=(ScopeGuard&& other) noexcept {
        if (this != &other) {
            // 如果当前还激活,先执行一次再接管(也可以选择直接 dismiss)
            if (active_) {
                func_();
            }
            func_   = std::move(other.func_);
            active_ = other.active_;
            other.dismiss();
        }
        return *this;
    }
    
    // 提前取消执行
    void dismiss() noexcept { active_ = false; }
    
    ~ScopeGuard() {
        if (active_) {
            func_();  // 析构时自动执行
        }
    }
    

    private: std::function func_; bool active_; };

    ```

C++14 Standard

  • What is a generic lambda? How does it differ from C++11 lambdas?

  • C++14 允许在 lambda 参数里使用 auto

  • What is an init-capture (generalized lambda capture) in C++14? Give an example use case.

  • cpp int x = 10; int y = 20; // C++14 auto f = [sum = x + y]() { std::cout << sum << "\n"; };

  • How is auto different in C++11/14 compared to the old auto keyword in C++98?

  • In C++98, auto was a storage-class specifier meaning “automatic storage duration”

  • In C++11 and C++14, auto was completely repurposed as a type-deduction keyword

  • Explain return type deduction for functions in C++14. How is it different from C++11 trailing return types?

  • C++11

    template <typename T, typename U>
    auto add(T a, U b) -> decltype(a + b) {
        return a + b;
    }
    
  • C++14

    auto add(auto a, auto b) {      // C++20 才支持参数 auto,这里假设模板
        return a + b;
    }
    template <typename T, typename U>
    auto add(T a, U b) {            // C++14
        return a + b;               // 编译器从 return 推导返回类型
    }
    
  • What is constexpr? How did its capabilities change from C++11 to C++14?

  • C++11 里,constexpr 函数几乎只能是 单一 return 表达式

    constexpr int square(int x) {
        return x * x;  // 必须是一个简单的 return 表达式
    }
    
  • C++14:relaxed constexpr —— 真正能写“正常代码”了

  • What is the difference between const and constexpr?

  • What are variable templates? Give an example.

  • C++14 新增的特性:模板不止可以用在类型和函数上,也可以用在变量上

  • cpp template <class T> constexpr T pi = T(3.1415926535897932385L); int main() { std::cout << pi<float> << "\n"; // float 版本 std::cout << pi<double> << "\n"; // double 版本 }

  • What are std::integer_sequence and std::index_sequence used for

  • std::integer_sequence is a compile-time sequence of integers, and std::index_sequence is just a shorthand for a sequence of std::size_t indices.

  • 在模板里“展开参数包”,尤其是对 std::tuple 做操作的时候, 你要对tuple的每个元素都操作一个函数。

  • ``` cpp template auto apply_impl(F&& f, Tuple&& t, std::index_sequence) { return f(std::get(std::forward(t))...); }

    template auto apply(F&& f, const std::tuple& t) { return apply_impl(std::forward(f), t, std::index_sequence_for{}); } ```

  • cpp using A = std::make_index_sequence<3>; // index_sequence<0,1,2> using B = std::index_sequence_for<int,double,char>; // index_sequence<0,1,2>

  • std::tie and std::make_tuple

  • std::tie(a1, b1) = t1; // a1 = 1, b1 = 2 ,创建一个 tuple of references,里面是 对已有变量的引用

  • 创建一个 owning tuple,里面存的是 拷贝/移动后得到的值

  • What are user-defined literals? Give some examples from the standard library.

  • User-defined literal(用户自定义字面量): 允许你为字面量(数字、字符串等)定义自己的后缀,让 123_kg"hello"_id 这样的东西变成合法的代码,并调用你写的函数。

  • ``` cpp long double operator"" _kg(long double v) { return v * 1000; // 转换成 gram }

    auto w = 1.2_kg; // 调用上面的 operator"" _kg ```

  • What is the purpose of the "..."s string literal? What header and namespace are required to use it?

  • The "..."s literal turns a string literal (C-style const char[N]) into a std::string object directly.

  • What is the purpose of the chrono literals such as 10ms or 2h? How do you enable them?

  • The chrono literals like 10ms, 1s or 2h construct std::chrono::duration objects directly, which makes time intervals much more readable and avoids magic numbers.

  • What does the [[deprecated]] attribute do? When would you use it?

  • attribute marks a function / variable / type as deprecated.

  • Explain binary literals and digit separators in C++14. Why are they helpful?

  • The separator has no runtime cost; it just makes large or complex numeric literals more readable

  • What is std::exchange used for?

  • Explain noexcept. When should you mark a function noexcept?

  • I typically mark functions noexcept when I can guarantee they won’t throw, especially move constructors, move assignment operators, and swap.

  • What is the difference between throw() and noexcept?

  • noexcept is the modern replacement. noexcept is simpler and can be used by the compiler for optimizations.

  • What is the “most vexing parse” and how does uniform initialization help with it?

  • X x(X()); → can be parsed as a function declaration (vexing parse).

  • X x{X{}};cannot be a function declaration, it is definitely object initialization.

  • What is SFINAE? Why is it important in template metaprogramming?

  • When do you need a virtual destructor?

  • What is a memory leak and how can you avoid it in modern C++?

  • Describe three important new features that C++14 adds on top of C++11.

  • Write a C++14 function that returns a std::vector<int> using auto return type deduction.

  • What are the advantages of using digit separators (') in numeric literals? Give an example.

  • Why is std::make_unique recommended over manually calling new when creating a std::unique_ptr?

C++17 Standard

  • What is the difference between std::enable_if and if constexpr (C++17) in terms of usage?
  • What is copy elision? How did the standard change in C++17 regarding copy elision?