Deducing Types
- 1. Template Type Deduction (Item 1)
- 2. Auto Type Deduction (Item 2)
- 3. Decltype (Item 3)
- 4. Viewing Deduced Types (Item 4)
- Multiple-Choice Questions
- Design Questions
- Test Cases
- Test 1: Array Decay
- Test 2: Universal Reference Deduction
- Test 3: decltype Behavior
- Answers and Explanations
- Multiple-Choice Answers
- Design Answers
1. Template Type Deduction (Item 1)
- Three Cases:
ParamType
is a reference/pointer (not universal reference):- Ignore references in
expr
; match remaining type toParamType
.
template<typename T> void f(T& param); const int cx = 0; f(cx); // T = const int, param = const int&
- Ignore references in
ParamType
is a universal reference (T&&
):- Lvalues deduce
T
as lvalue reference; rvalues deduceT
as non-reference.
template<typename T> void f(T&& param); int x = 0; f(x); // T = int&, param = int& (lvalue) f(27); // T = int, param = int&& (rvalue)
- Lvalues deduce
ParamType
is by-value (neither pointer nor reference):- Strip
const
/volatile
; decay arrays/functions to pointers.
template<typename T> void f(T param); const char arr[] = "test"; f(arr); // T = const char* (array decays to pointer)
- Strip
2. Auto Type Deduction (Item 2)
- Follows template deduction rules except for braced initializers:
auto x = {1, 2, 3}; // x = std::initializer_list<int> template<typename T> void f(T param); f({1, 2, 3}); // Error: cannot deduce T
3. Decltype (Item 3)
decltype
returns the declared type of an entity or expression:int x = 0; decltype(x) y = x; // y = int decltype((x)) z = y; // z = int& (expression (x) is lvalue)
4. Viewing Deduced Types (Item 4)
- Use
typeid
(may strip references) orBoost.TypeIndex
:#include <boost/type_index.hpp> template<typename T> void printType() {std::cout << boost::typeindex::type_id_with_cvr<T>().pretty_name(); }
Multiple-Choice Questions
-
What is the type of
param
intemplate<typename T> void f(T&& param)
when called withint x = 0; f(x);
?- A)
int
- B)
int&
- C)
int&&
- D)
const int&
Answer: B)
int&
Explanation:x
is an lvalue; universal reference deducesT
asint&
. - A)
-
Given
const char name[] = "Hello"; auto arr = name;
, what isarr
’s type?- A)
const char*
- B)
const char[6]
- C)
const char(&)[6]
- D)
char*
Answer: A)
const char*
Explanation: Arrays decay to pointers in by-valueauto
deduction. - A)
-
Which code causes a compilation error?
// 1: auto x = {1}; // 2: template<typename T> void f(T param); f({1});
- A) 1
- B) 2
- C) Both
- D) Neither
Answer: B) 2
Explanation: Braced initializers cannot deduceT
in templates.
-
What is
decltype((x))
ifint x = 0;
?- A)
int
- B)
int&
- C)
int&&
- D)
const int
Answer: B)
int&
Explanation:(x)
is an lvalue expression;decltype
yieldsint&
. - A)
-
Which code deduces
T
asint[3]
?int arr[3]; // 1: template<typename T> void f(T& param); f(arr); // 2: template<typename T> void f(T param); f(arr);
- A) 1
- B) 2
- C) Both
- D) Neither
Answer: A) 1
Explanation: References preserve array types; by-value decays to pointer.
Design Questions
- Write a template function
arraySize
returning the size of an array.template<typename T, size_t N> constexpr size_t arraySize(T (&)[N]) { return N; }int main() {int arr[] = {1, 2, 3};static_assert(arraySize(arr) == 3); }
- Use
decltype
to declare a reference variabley
bound tox
withoutauto&
.int x = 10; decltype((x)) y = x; // y = int&
- Fix the error in
print({1, 2, 3})
:// Original (error): template<typename T> void print(T param) {} print({1, 2, 3});// Fixed: void print(std::initializer_list<int> param) {}
- Design a
forwarder
to perfectly forward arguments.template<typename F, typename... Args> auto forwarder(F&& func, Args&&... args) {return std::forward<F>(func)(std::forward<Args>(args)...); }
- Create a type trait
IsReference
to detect references.template<typename T> struct IsReference : std::false_type {};template<typename T> struct IsReference<T&> : std::true_type {};template<typename T> struct IsReference<T&&> : std::true_type {};
Test Cases
Test 1: Array Decay
#include <iostream>
#include <type_traits>template<typename T>
void checkArray(T param) {if (std::is_array_v<T>)std::cout << "Array preserved!\n";elsestd::cout << "Array decayed to pointer.\n";
}int main() {int arr[3];checkArray(arr); // Output: "Array decayed to pointer."
}
Test 2: Universal Reference Deduction
#include <iostream>
#include <type_traits>template<typename T>
void checkRef(T&& param) {if (std::is_lvalue_reference_v<T&&>)std::cout << "Lvalue reference\n";elsestd::cout << "Rvalue reference\n";
}int main() {int x = 0;checkRef(x); // Lvalue referencecheckRef(27); // Rvalue reference
}
Test 3: decltype Behavior
#include <iostream>
#include <type_traits>int main() {int x = 0;decltype((x)) y = x; // y = int&static_assert(std::is_same_v<decltype(y), int&>);
}
Answers and Explanations
Multiple-Choice Answers
- B - Universal reference with lvalue deduces to lvalue reference.
- A - By-value
auto
decays arrays to pointers. - B - Braced initializers cannot deduce template types.
- B -
decltype
on lvalue expression yields reference. - A - References preserve array types.
Design Answers
arraySize
uses reference to deduce array size.decltype((x))
captures lvalue reference.- Explicit
std::initializer_list
fixes template deduction. forwarder
uses perfect forwarding.IsReference
specializes forT&
andT&&
.