C++ Advanced Templates Quiz
40 in-depth questions covering advanced C++ template metaprogramming with SFINAE, variadic templates, constexpr, and compile-time computation — with 16 code examples to solidify understanding.
Question 1
What is SFINAE (Substitution Failure is Not An Error)?
template <typename T>
auto func(T t) -> decltype(t.begin(), void()) {
// Only enabled if T has begin() member
std::cout << "Container\n";
}
template <typename T>
auto func(T t) -> decltype(t++, void()) {
// Only enabled if T supports postfix ++
std::cout << "Iterator\n";
}
// SFINAE: if substitution fails, overload is discarded, not errorQuestion 2
What is std::enable_if and how does it work?
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
void func(T value) {
std::cout << "Integral: " << value << std::endl;
}
// Only enabled for integral types
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
void func(T value) {
std::cout << "Floating: " << value << std::endl;
}
func(42); // Calls integral version
func(3.14); // Calls floating versionQuestion 3
What are variadic templates?
template <typename... Args>
void print_all(Args... args) {
// Parameter pack expansion
(std::cout << ... << args) << std::endl;
}
// Recursive unpacking
template <typename T, typename... Rest>
void print_recursive(T first, Rest... rest) {
std::cout << first << " ";
print_recursive(rest...);
}
print_all(1, 2.5, "hello"); // 1 2.5 helloQuestion 4
What are fold expressions in C++17?
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // Binary fold: ((arg1 + arg2) + arg3) + ...
}
template <typename... Args>
auto all_true(Args... args) {
return (args && ...); // Left fold with &&
}
template <typename... Args>
auto any_true(Args... args) {
return (... || args); // Right fold with ||
}
std::cout << sum(1, 2, 3, 4); // 10Question 5
What is template specialization?
// Primary template
template <typename T>
struct Wrapper {
void print() { std::cout << "Generic\n"; }
};
// Full specialization for int
template <>
struct Wrapper<int> {
void print() { std::cout << "Int specialization\n"; }
};
// Partial specialization for pointers
template <typename T>
struct Wrapper<T*> {
void print() { std::cout << "Pointer specialization\n"; }
};
Wrapper<double> w1; w1.print(); // Generic
Wrapper<int> w2; w2.print(); // Int specialization
Wrapper<int*> w3; w3.print(); // Pointer specializationQuestion 6
What is constexpr in template metaprogramming?
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
// Compile-time computation
template <int N>
struct Factorial {
static constexpr int value = N * Factorial<N-1>::value;
};
template <>
struct Factorial<0> {
static constexpr int value = 1;
};
std::array<int, Factorial<5>::value> arr; // Size computed at compile-timeQuestion 7
What is tag dispatch?
struct integral_tag {};
struct floating_tag {};
struct other_tag {};
template <typename T>
struct tag {
using type = other_tag;
};
template <>
struct tag<int> {
using type = integral_tag;
};
void process_impl(int value, integral_tag) {
std::cout << "Processing int: " << value << std::endl;
}
void process_impl(double value, floating_tag) {
std::cout << "Processing double: " << value << std::endl;
}
template <typename T>
void process(T value) {
process_impl(value, typename tag<T>::type{});
}Question 8
What are template template parameters?
template <template <typename, typename> class Container, typename T, typename Alloc>
class Wrapper {
Container<T, Alloc> data;
public:
void add(const T& value) { data.push_back(value); }
};
// Usage
Wrapper<std::vector, int, std::allocator<int>> vec_wrapper;
Wrapper<std::deque, std::string, std::allocator<std::string>> deque_wrapper;
// Container type is parameterized
template <typename T>
using MyVector = Wrapper<std::vector, T, std::allocator<T>>;Question 9
What is CRTP (Curiously Recurring Template Pattern)?
template <typename Derived>
class Base {
public:
void interface() {
static_cast<Derived*>(this)->implementation();
}
void common_function() {
std::cout << "Common behavior\n";
}
};
class Concrete : public Base<Concrete> {
public:
void implementation() {
std::cout << "Concrete implementation\n";
}
};
Concrete c;
c.interface(); // Calls Concrete::implementation()
c.common_function(); // Calls Base::common_function()Question 10
What is decltype and how is it used in templates?
template <typename T, typename U>
auto add(T t, U u) -> decltype(t + u) {
return t + u;
}
// Trailing return type with decltype
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin();
decltype(*it) value = *it; // int
decltype(auto) func() {
return 42; // Return type is int
}Question 11
What is template argument deduction?
// Function template deduction
template <typename T>
void func(T param) {
std::cout << typeid(T).name() << std::endl;
}
func(42); // T = int
func(3.14); // T = double
func("hello"); // T = const char*
// Class template deduction (C++17)
std::pair p(1, 2.5); // std::pair<int, double>
std::vector v{1, 2, 3}; // std::vector<int>Question 12
What is partial template specialization?
// Primary template
template <typename T, typename U>
struct Pair {
T first;
U second;
};
// Partial specialization for same types
template <typename T>
struct Pair<T, T> {
T both;
Pair(T val) : both(val) {}
};
// Partial specialization for pointers
template <typename T, typename U>
struct Pair<T*, U*> {
T* first;
U* second;
Pair(T* f, U* s) : first(f), second(s) {}
};
Pair<int, double> p1; // Primary
Pair<int, int> p2(42); // Same types specialization
Pair<int*, double*> p3; // Pointer specializationQuestion 13
What is if constexpr in C++17?
template <typename T>
auto process(T value) {
if constexpr (std::is_integral_v<T>) {
return value * 2; // Compile-time branch
} else if constexpr (std::is_floating_point_v<T>) {
return value * 2.0;
} else {
static_assert(false, "Unsupported type");
}
}
// Only true branch is compiled
int result = process(42); // Only int branch compiled
double result2 = process(3.14); // Only double branch compiledQuestion 14
What are type traits?
#include <type_traits>
// Primary template
struct is_pointer : std::false_type {};
// Specialization for pointers
template <typename T>
struct is_pointer<T*> : std::true_type {};
// Usage
static_assert(is_pointer<int*>::value, "int* should be pointer");
static_assert(!is_pointer<int>::value, "int should not be pointer");
// Standard type traits
std::cout << std::is_integral<int>::value; // true
std::cout << std::is_floating_point<double>::value; // trueQuestion 15
What is parameter pack expansion?
template <typename... Args>
void print_all(Args... args) {
// Expand with comma separator
std::cout << ((std::to_string(args) + " ") + ...);
}
// Expand into function calls
template <typename... Args>
void call_functions(Args... funcs) {
(funcs(), ...); // Call each function
}
// Expand into template instantiations
template <typename... Types>
struct Tuple {
std::tuple<Types...> data;
};
print_all(1, 2, 3); // "1 2 3 "Question 16
What is template recursion and compile-time computation?
// Factorial at compile-time
template <int N>
struct Factorial {
static constexpr int value = N * Factorial<N-1>::value;
};
template <>
struct Factorial<0> {
static constexpr int value = 1;
};
// Fibonacci
template <int N>
struct Fibonacci {
static constexpr int value = Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};
template <>
struct Fibonacci<0> {
static constexpr int value = 0;
};
template <>
struct Fibonacci<1> {
static constexpr int value = 1;
};
std::array<int, Factorial<5>::value> arr; // Size = 120Question 17
What is perfect forwarding in templates?
template <typename T>
void wrapper(T&& arg) {
// Forward preserves value category
func(std::forward<T>(arg));
}
void func(int& x) { std::cout << "Lvalue\n"; }
void func(int&& x) { std::cout << "Rvalue\n"; }
int x = 42;
wrapper(x); // Calls func(int&) - lvalue preserved
wrapper(42); // Calls func(int&&) - rvalue preserved
wrapper(std::move(x)); // Calls func(int&&) - rvalue preservedQuestion 18
What is static_assert in template metaprogramming?
template <typename T>
class Container {
static_assert(std::is_copy_constructible_v<T>,
"T must be copy constructible");
static_assert(sizeof(T) <= 1000,
"T is too large for this container");
T data;
};
// Compilation fails with clear error messages
Container<NonCopyable> bad1; // Error: T must be copy constructible
Container<HugeStruct> bad2; // Error: T is too large...Question 19
What is template non-type parameters?
// Integer parameters
template <int Size>
class Array {
int data[Size];
};
// Pointer parameters
template <char* const Str>
struct StringLiteral {
static constexpr const char* value = Str;
};
// Usage
Array<10> arr; // Size = 10
StringLiteral<"hello"> str; // Str = "hello"
// Template variables (C++14)
template <auto Value>
constexpr auto constant = Value;
auto x = constant<42>; // x = 42Question 20
What is the difference between typename and class in template parameters?
// Completely interchangeable
template <typename T> class A {}; // Same as:
template <class T> class B {}; // Same
// But typename required in dependent contexts
template <typename T>
void func() {
typename T::iterator it; // typename required
T::value_type val; // No typename needed in C++20
}
// typename in template template parameters
template <template <typename> typename Container>
class Wrapper {
Container<int> data;
};Question 21
What is template argument deduction for constructors?
template <typename T>
struct Wrapper {
T value;
// Deduction guide
template <typename U>
Wrapper(U&& val) -> Wrapper<std::decay_t<U>>;
};
// Usage - T deduced from constructor argument
Wrapper w1(42); // Wrapper<int>
Wrapper w2(3.14); // Wrapper<double>
Wrapper w3("hello"); // Wrapper<const char*>
// Without deduction guide, would need:
Wrapper<int> w4(42); // Explicit type requiredQuestion 22
What is dependent name lookup in templates?
template <typename T>
void func(T t) {
t.begin(); // Dependent name - depends on T
// May need typename for dependent types
typename T::iterator it = t.begin();
// Two-phase lookup: names looked up twice
// 1. Template definition time (non-dependent names)
// 2. Template instantiation time (dependent names)
}
struct Container {
void begin() { std::cout << "begin\n"; }
using iterator = int*;
};
func(Container{}); // Dependent names resolved at instantiationQuestion 23
What is template instantiation and when does it occur?
// Template definition
template <typename T>
T add(T a, T b) { return a + b; }
// Point of instantiation
int main() {
int result = add(1, 2); // Template instantiated here with T=int
// Implicit instantiation
double result2 = add(1.0, 2.0); // T=double
// Explicit instantiation (rare)
template int add<int>(int, int);
}
// Each unique set of template arguments creates separate instantiationQuestion 24
What is ADL (Argument Dependent Lookup)?
namespace MyNamespace {
class MyClass {};
void func(MyClass) {
std::cout << "MyNamespace::func\n";
}
}
// ADL finds func in MyNamespace
MyNamespace::MyClass obj;
func(obj); // Calls MyNamespace::func via ADL
// Without ADL, would need:
MyNamespace::func(obj); // Explicit qualificationQuestion 25
What is template default arguments?
// Function template defaults
template <typename T = int>
void func(T value = 0) {
std::cout << value << std::endl;
}
func(); // T=int, value=0
func(3.14); // T=double, value=3.14
// Class template defaults
template <typename T = int, typename Alloc = std::allocator<T>>
class Vector {
// ...
};
Vector<> v1; // Vector<int, allocator<int>>
Vector<double> v2; // Vector<double, allocator<double>>Question 26
What is the difference between function templates and class templates?
// Function template - argument deduction
template <typename T>
T max(T a, T b) { return a > b ? a : b; }
int result = max(1, 2); // T deduced as int
// Class template - explicit or deduced
template <typename T>
class Stack {
T data;
};
Stack<int> s1; // Explicit
Stack s2(42); // Deduced (C++17)
// Function templates can be specialized
// Class templates can have partial specializationQuestion 27
What is template metaprogramming recursion depth limits?
// Deep recursion may hit compiler limits
template <int N>
struct Factorial {
static constexpr int value = N * Factorial<N-1>::value;
};
template <>
struct Factorial<0> {
static constexpr int value = 1;
};
// Very deep recursion may fail
// Factorial<1000>::value; // May exceed compiler limits
// Solution: Use constexpr functions for deep recursion
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr int result = factorial(1000); // OKQuestion 28
What is the difference between constexpr and const?
const int runtime_const = 42; // Runtime constant
constexpr int compile_const = 42; // Compile-time constant
// constexpr can be used in template parameters
template <constexpr int N>
class Array {
int data[N]; // N must be compile-time constant
};
// constexpr functions
constexpr int square(int x) { return x * x; }
int arr[square(3)]; // OK - computed at compile-time
Array<square(4)> arr2; // OK - used as template parameterQuestion 29
What is template alias (using) in C++11?
// Type alias
template <typename T>
using Vec = std::vector<T, std::allocator<T>>;
Vec<int> v1; // std::vector<int, allocator<int>>
// Template alias
template <typename T>
using FuncPtr = void(*)(T);
FuncPtr<int> fp; // void(*)(int)
// Complex aliases
template <typename Key, typename Value>
using MapAllocator = std::allocator<std::pair<const Key, Value>>;
template <typename Key, typename Value>
using MyMap = std::map<Key, Value, std::less<Key>, MapAllocator<Key, Value>>;Question 30
What is the difference between template specialization and overloading?
// Overloading - different functions
template <typename T>
void func(T) { std::cout << "Generic\n"; }
void func(int) { std::cout << "Int overload\n"; }
// Specialization - same template, different implementation
template <typename T>
struct Wrapper { void print() { std::cout << "Generic\n"; } };
template <>
struct Wrapper<int> { void print() { std::cout << "Int specialization\n"; } };
func(42); // Calls overload
func(3.14); // Calls template
Wrapper<double> w1; w1.print(); // Generic
Wrapper<int> w2; w2.print(); // SpecializationQuestion 31
What is the template keyword in dependent contexts?
template <typename T>
struct Container {
using value_type = T;
template <typename U>
void func(U val) {}
};
template <typename T>
void process(Container<T> c) {
// Dependent name - need 'template'
c.template func<int>(42);
// Dependent type - need 'typename'
typename Container<T>::value_type val;
// Non-dependent names don't need keywords
std::cout << "Processing\n";
}Question 32
What is universal reference (forwarding reference)?
template <typename T>
void func(T&& arg) { // Universal reference
// T&& is rvalue reference if T is concrete type
// T&& is forwarding reference if T is template parameter
}
// Perfect forwarding
template <typename T>
void wrapper(T&& arg) {
func(std::forward<T>(arg)); // Preserves value category
}
int x = 42;
wrapper(x); // arg is lvalue reference
wrapper(42); // arg is rvalue reference
wrapper(std::move(x)); // arg is rvalue referenceQuestion 33
What is the difference between auto and decltype(auto)?
auto x = 42; // int
const auto& ref = x; // const int&
auto func() {
return 42; // Return type is int
}
decltype(auto) func2() {
return 42; // Return type is int (same as auto here)
}
int y = 42;
int& ref_y = y;
auto x2 = ref_y; // int (reference stripped)
decltype(auto) x3 = ref_y; // int& (reference preserved)
decltype(auto) func3() {
static int x = 42;
return (x); // Return type is int& (reference to x)
}Question 34
What is template argument substitution and failure?
// Substitution failure example
template <typename T>
auto begin(T& t) -> decltype(t.begin()) {
return t.begin();
}
// For types without begin(), substitution fails
// SFINAE: overload is discarded, not error
struct HasBegin {
int* begin() { return nullptr; }
};
struct NoBegin {
// No begin() member
};
HasBegin obj1;
begin(obj1); // OK - substitution succeeds
NoBegin obj2;
// begin(obj2); // Substitution fails, overload discarded
// If no other overloads, compilation errorQuestion 35
What is the difference between member function templates and regular member functions?
class Container {
public:
// Regular member function
void assign(size_t count, int value) {
// Specific implementation
}
// Member function template
template <typename InputIt>
void assign(InputIt first, InputIt last) {
// Generic implementation
}
};
Container c;
c.assign(5, 42); // Calls regular member
c.assign(vec.begin(), vec.end()); // Calls template member
// Template members can be specialized
// Regular members cannot be templated on class levelQuestion 36
What is the difference between explicit and implicit template instantiation?
// Implicit instantiation
template <typename T>
T add(T a, T b) { return a + b; }
int result = add(1, 2); // Implicitly instantiates add<int>
// Explicit instantiation
template int add<int>(int, int); // Forces instantiation
// Explicit instantiation definition
template <typename T>
class MyClass {
void func();
};
// In .cpp file:
template class MyClass<int>; // Instantiate entire class
template void MyClass<int>::func(); // Instantiate specific memberQuestion 37
What is the difference between std::decay and std::remove_reference?
using T1 = std::remove_reference_t<int&>; // int
using T2 = std::remove_reference_t<int&&>; // int
using T3 = std::remove_reference_t<int>; // int
using T4 = std::decay_t<int&>; // int
using T5 = std::decay_t<int&&>; // int
using T6 = std::decay_t<int>; // int
using T7 = std::decay_t<int[5]>; // int*
using T8 = std::decay_t<const int>; // int
using T9 = std::decay_t<void(int)>; // void(*)(int)Question 38
What is the difference between template metaprogramming and constexpr programming?
// Template metaprogramming
template <int N>
struct Factorial {
static constexpr int value = N * Factorial<N-1>::value;
};
template <>
struct Factorial<0> {
static constexpr int value = 1;
};
// Constexpr programming
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
// Usage
std::array<int, Factorial<5>::value> arr1; // Template
constexpr int val = factorial(5); // Constexpr
std::array<int, factorial(5)> arr2; // ConstexprQuestion 39
What is the difference between std::enable_if and concepts (C++20)?
// enable_if approach
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
void func(T val) {}
// Concepts approach (C++20)
template <std::integral T>
void func(T val) {}
// More expressive concepts
template <typename T>
concept Addable = requires(T a, T b) { a + b; };
template <Addable T>
T add(T a, T b) { return a + b; }Question 40
What are the fundamental principles for effective template metaprogramming in C++?
