Menu
CoddyTech

Modern C++ Interview Questions

auto, lambdas, move semantics, perfect forwarding, constexpr and C++17 and C++20 features.

10 questions
  1. What are the main features added in C++11, C++14, C++17 and C++20?

    StandardHeadline features
    C++11auto, lambdas, move semantics, smart pointers, nullptr, range-based for, constexpr, std::thread
    C++14std::make_unique, generic lambdas
    C++17structured bindings, std::optional, std::string_view, if constexpr, guaranteed copy elision
    C++20concepts, ranges, coroutines, modules, <=>, std::format
    C++23std::expected, std::print
  2. Does auto keep the reference when initialized from an int&?

    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        int x = 5;
        int& r = x;
        auto a = r;
        auto& b = r;
        a = 100;
        b = 7;
        cout << x << "\n";
    }

    It prints 7. auto drops the reference, so a is a copy and a = 100 does not touch x. auto& b = r; keeps the reference, so b = 7 changes x.

    The same rule makes for (auto s : names) copy every string; write const auto&.

  3. What is a lambda expression in C++?

    An anonymous function object written inline, usually to pass to an algorithm: [capture](parameters) { body }.

    C++
    #include <algorithm>
    #include <cstdlib>
    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main() {
        vector<int> v = {7, -2, 9, 4, -5};
        int limit = 5;
        auto above = count_if(v.begin(), v.end(), [limit](int x) { return x > limit; });
        sort(v.begin(), v.end(), [](int a, int b) { return abs(a) < abs(b); });
        cout << above << " | ";
        for (int x : v) cout << x << " ";
        cout << "\n";
    }

    This prints 2 | -2 4 -5 7 9. The compiler generates a class with an operator(), and captures become its members. Store a lambda in auto, or in std::function when you need one fixed type.

  4. What does a lambda see after x changes: [x] or [&x]?

    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        int x = 1;
        auto byVal = [x]() { return x; };
        auto byRef = [&x]() { return x; };
        x = 5;
        cout << byVal() << " " << byRef() << "\n";
    }

    It prints 1 5. [x] stores a copy of x when the lambda is created; [&x] reads the current value.

    A by-value capture is const inside the lambda unless you add mutable, and a by-reference capture that outlives the variable dangles. More in the lambdas docs.

  5. What are move semantics and rvalue references?

    An rvalue reference T&& binds to temporaries, so a class can define a move constructor that steals a temporary's resources instead of copying them.

    C++
    #include <iostream>
    #include <string>
    #include <utility>
    #include <vector>
    
    int main() {
        std::vector<std::string> v;
        std::string s = "a long string that does not fit in the small buffer";
        v.push_back(s);              // copy: s keeps its text
        v.push_back(std::move(s));   // move: v takes s's buffer
        std::cout << v.size() << " [" << s << "]\n";
    }

    With GCC this prints 2 []. A moved-from object is "valid but unspecified", so do not rely on its value. Mark move constructors noexcept, or containers copy instead.

  6. What does std::move actually do? What is perfect forwarding?

    std::move moves nothing: it casts to an rvalue reference so overload resolution picks the move constructor. Perfect forwarding passes an argument on with its original value category: in a template T&& is a forwarding reference, and std::forward<T>(arg) restores it.

    C++
    #include <iostream>
    #include <string>
    #include <utility>
    using namespace std;
    
    void take(const string&) { cout << "lvalue\n"; }
    void take(string&&) { cout << "rvalue\n"; }
    
    template <typename T>
    void relay(T&& arg) {
        take(std::forward<T>(arg));
    }
    
    int main() {
        string s = "x";
        relay(s);
        relay(string("y"));
        relay(std::move(s));
    }

    This prints lvalue, rvalue, rvalue. Without std::forward all three print lvalue, because a named parameter is an lvalue.

  7. How do std::optional and structured bindings (C++17) make code clearer?

    std::optional<T> holds a T or nothing, replacing sentinels like -1. Structured bindings unpack pairs, tuples and structs into named variables.

    C++
    #include <iostream>
    #include <map>
    #include <optional>
    #include <string>
    using namespace std;
    
    optional<int> findAge(const map<string, int>& ages, const string& name) {
        if (auto it = ages.find(name); it != ages.end()) return it->second;
        return nullopt;
    }
    
    int main() {
        map<string, int> ages = {{"ana", 31}, {"raj", 27}};
        for (const auto& [name, age] : ages) cout << name << ":" << age << " ";
        cout << "\n";
        cout << findAge(ages, "raj").value_or(-1) << " "
             << findAge(ages, "zoe").has_value() << "\n";
    }

    This prints ana:31 raj:27 and then 27 0. The if (auto it = ...; cond) form, also C++17, keeps it scoped to the if.

  8. What is constexpr, and how is it different from const?

    const means the value does not change after initialization, which may happen at runtime. constexpr means it can be evaluated at compile time; a constexpr function is guaranteed to run at compile time only where a constant is required, such as a constexpr variable, static_assert or an array size.

    C++
    #include <iostream>
    
    constexpr long long factorial(int n) {
        return n <= 1 ? 1 : n * factorial(n - 1);
    }
    
    int main() {
        static_assert(factorial(5) == 120);
        constexpr auto f10 = factorial(10);
        int n = 6;
        std::cout << f10 << " " << factorial(n) << "\n";
    }

    This prints 3628800 720: f10 was computed by the compiler, factorial(n) at runtime. C++20 adds consteval, which must run at compile time. See the constexpr reference.

  9. What do concepts, ranges and the spaceship operator in C++20 look like?

    These are shown as plain code because this page's runner compiles C++17; use g++ -std=c++20.

    #include <compare>
    #include <concepts>
    #include <ranges>
    #include <vector>
    
    // Concepts: constrain a template, with readable errors
    template <std::integral T>
    T half(T x) { return x / 2; }
    
    // Three-way comparison: one defaulted operator gives ==, !=, <, <=, >, >=
    struct Version {
        int major, minor;
        auto operator<=>(const Version&) const = default;
    };
    
    // Ranges: lazy, composable views instead of iterator pairs
    std::vector<int> v = {1, 2, 3, 4, 5, 6};
    auto squaresOfEvens = v
        | std::views::filter([](int x) { return x % 2 == 0; })
        | std::views::transform([](int x) { return x * x; });   // 4 16 36
    

    C++20 also brought coroutines, modules, std::format and std::span.

  10. What is the difference between enum and enum class in C++?

    An enum class (C++11) is scoped and strongly typed: its names are written Status::Ok, so two enums can both have an Ok, and it never converts to int silently. A plain enum leaks its names into the surrounding scope and converts implicitly.

    C++
    #include <iostream>
    using namespace std;
    
    enum Color { Red, Green, Blue };                // unscoped: names leak, converts to int
    enum class Status : char { Ok, Failed, Busy };  // scoped, with a chosen underlying type
    
    int main() {
        int c = Blue;                     // implicit conversion: allowed
        Status s = Status::Failed;        // the name must be qualified
        // int bad = s;                   // error: no implicit conversion
        int code = static_cast<int>(s);
        cout << c << " " << code << "\n";
    }

    It prints 2 1. Both kinds can fix the underlying type (: char); an enum class defaults to int, so it can be forward-declared without one. C++20's using enum Status; brings the names into scope when the qualification gets noisy. See enums.

Coddy programming languages illustration

Learn to code with Coddy

Get started