两阶段查找(Two-Phase Lookup)
原理:
模板在编译时分两个阶段处理:
代码示例:
#include
template<typename T>
void foo(T t) {
// 非依赖名称:在编译时检查
bar(t); // 第一阶段查找bar(),若未声明则报错
// 依赖名称:在第二阶段实例化时查找
t.func();
}
void bar(int) { std::cout << "bar(int)\n"; }
struct Data {
void func() const { std::cout << "Data::func()\n"; }
};
int main() {
foo(42); // 实例化foo,调用bar(int)和Data::func()
return 0;
}
测试用例:
// 输出:
// bar(int)
// Data::func()
代码示例:
template<typename T>
void baz() { /* ... */ }
void test() {
baz<int>(); // 触发baz的实例化
}
// POI位于test()之后,此处可访问baz
测试用例:
// 正确:POI在test()之后
代码示例:
template<typename T>
T add(T a, T b) { return a + b; }
// 显式实例化int版本
template int add<int>(int, int);
int main() {
add(1, 2); // 使用显式实例化的版本
return 0;
}
测试用例:
// 输出:3
if constexpr
(C++17)代码示例:
template<typename T>
auto get_value(const T& t) {
if constexpr (std::is_pointer_v<T>) {
return *t;
} else {
return t;
}
}
int main() {
int x = 5;
int* p = &x;
std::cout << get_value(x) << "\n"; // 输出5
std::cout << get_value(p) << "\n"; // 输出5
return 0;
}
测试用例:
// 输出:
// 5
// 5
综合测试程序
以下将所有知识点整合到一个测试程序中:
#include
#include
// 两阶段查找示例
template<typename T>
void foo(T t) {
bar(t); // 非依赖名称,第一阶段需可见
t.func(); // 依赖名称,第二阶段实例化时查找
}
void bar(int) { std::cout << "bar(int)\n"; }
struct Data {
void func() const { std::cout << "Data::func()\n"; }
};
// 显式实例化示例
template<typename T>
T add(T a, T b) { return a + b; }
template int add<int>(int, int); // 显式实例化int版本
// 编译期if constexpr示例
template<typename T>
auto get_value(const T& t) {
if constexpr (std::is_pointer_v<T>) {
return *t;
} else {
return t;
}
}
int main() {
// 测试两阶段查找
foo(42); // 调用bar(int)和Data::func()
// 测试显式实例化
std::cout << add(3, 5) << "\n"; // 输出8
// 测试编译期if constexpr
int x = 10;
int* p = &x;
std::cout << get_value(x) << "\n"; // 输出10
std::cout << get_value(p) << "\n"; // 输出10
return 0;
}
输出结果:
bar(int)
Data::func()
8
10
10
题目1:模板实例化的基本流程是?
A. 解析模板定义时立即生成代码
B. 使用时按需生成代码(按需实例化)
C. 显式实例化指令触发代码生成
D. 所有模板参数必须显式指定
答案:B, C
详解:
template class MyClass;
可显式触发实例化。题目2:两阶段查找(Two-Phase Lookup)的规则是?
A. 非依赖名称在模板定义时解析
B. 依赖名称在模板实例化时解析
C. 所有名称都在实例化时解析
D. ADL 仅在实例化阶段生效
答案:A, B
详解:
T::func
)在实例化时结合实参类型解析。题目3:显式实例化声明(extern template
)的作用是?
A. 防止模板在当前翻译单元实例化
B. 强制模板在其他地方实例化
C. 减少编译时间
D. 提升链接效率
答案:A, C
详解:
题目4:编译期if constexpr
(C++17)与运行期if
的关键区别是?
A. 编译期分支可能被完全剔除
B. 运行期if
可处理非constexpr
条件
C. 编译期if
必须满足常量表达式
D. 两者均可用于模板元编程
答案:A, B, C
详解:
if
无此限制。constexpr if
条件需在编译期可求值。if
无法参与模板特化。题目5:显式实例化与显式特化的区别是?
A. 显式实例化生成通用代码
B. 显式特化为特定模式提供定制实现
C. 显式实例化优先级高于显式特化
D. 显式特化需在命名空间作用域声明
答案:A, B
详解:
template class MyClass;
生成MyClass
的代码。template<> void MyClass::func() {...}
定制int
版本的实现。题目6:以下哪种情况会导致模板实例化失败?
A. 成员函数模板推导失败
B. 构造函数默认参数未定义
C. 虚函数表生成时依赖未实例化的类型
D. 静态成员变量未显式初始化
答案:A, B, C
详解:
题目7:类模板成员的显式实例化方式是?
A. template void MyClass
B. template MyClass
C. extern template void MyClass
D. template<> void MyClass
答案:A
详解:
template
关键字。extern
用于声明而非定义。题目8:编译期if constexpr
的典型应用场景是?
A. 实现类型萃取(Type Traits)
B. 条件编译不同代码路径
C. 优化递归模板展开
D. 替代宏定义
答案:A, B, C
详解:
constexpr if
无法完全替代宏的语义。题目9:模板实例化的存储优化技术包括?
A. 链接器去重(Linker Deduplication)
B. 内联展开(Inlining)
C. 空基类优化(EBO)
D. 全局变量合并
答案:A, B
详解:
题目10:以下代码的输出是?
template<typename T> void foo(T) { cout << "T" << endl; }
template<> void foo<int>(int) { cout << "int" << endl; }
extern template void foo<double>(double);
int main() {
foo(1); // A
foo(1.0); // B
foo('c'); // C
}
A. int
B. T
C. T
答案:A. int, B. T, C. T
详解:
double
,按需实例化通用版本。char
,调用通用版本。题目1:实现一个线程安全的单例模式,要求支持任意类型T
,并利用显式实例化优化性能。
#include
#include
template<typename T>
class Singleton {
private:
static T* instance;
static std::once_flag flag;
Singleton() = default;
~Singleton() = default;
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static T& getInstance() {
std::call_once(flag, []{
instance = new T();
});
return *instance;
}
};
// 显式实例化常用类型以优化性能
template<> Singleton<int>::instance = nullptr;
template<> std::once_flag Singleton<int>::flag;
int main() {
Singleton<int>& si = Singleton<int>::getInstance();
Singleton<std::string>& ss = Singleton<std::string>::getInstance();
return 0;
}
题目2:编写一个模板元函数is_pointer_v
,检测类型是否为指针,并利用编译期if constexpr
优化性能。
#include
#include
template<typename T>
constexpr bool is_pointer_v = false;
template<typename T>
constexpr bool is_pointer_v<T*> = true;
template<typename T>
void checkPointer(T val) {
if constexpr (is_pointer_v<T>) {
std::cout << "Pointer type" << std::endl;
} else {
std::cout << "Non-pointer type" << std::endl;
}
}
int main() {
int a;
int* p = &a;
checkPointer(a); // 输出 Non-pointer type
checkPointer(p); // 输出 Pointer type
return 0;
}
题目3:实现一个泛型缓存类Cache
,支持通过键值快速访问对象,利用显式实例化减少模板代码膨胀。
#include
#include
template<typename Key, typename Value>
class Cache {
private:
std::unordered_map<Key, Value> storage;
public:
void set(const Key& key, const Value& value) {
storage[key] = value;
}
Value get(const Key& key) {
return storage[key];
}
};
// 显式实例化常用组合
template class Cache<std::string, int>;
template class Cache<int, std::string>;
int main() {
Cache<std::string, int> intCache;
intCache.set("age", 25);
std::cout << intCache.get("age") << std::endl; // 输出 25
Cache<int, std::string> strCache;
strCache.set(1, "one");
std::cout << strCache.get(1) << std::endl; // 输出 one
return 0;
}
题目4:设计一个支持多种序列化协议的模板类Serializer
,利用显式实例化适配不同协议。
#include
#include
enum class Protocol { JSON, XML };
template<Protocol P>
class Serializer {
public:
static std::string serialize(int value) {
if constexpr (P == Protocol::JSON) {
return "{\"value\":" + std::to_string(value) + "}";
} else {
return "" + std::to_string(value) + "";
}
}
};
// 显式实例化常用协议
template class Serializer<Protocol::JSON>;
template class Serializer<Protocol::XML>;
int main() {
std::cout << Serializer<Protocol::JSON>::serialize(42) << std::endl; // 输出 {"value":42}
std::cout << Serializer<Protocol::XML>::serialize(42) << std::endl; // 输出 42
return 0;
}
题目5:实现一个类型萃取工具TypeTraits
,利用编译期if constexpr
简化类型判断逻辑。
#include
#include
template<typename T>
struct TypeTraits {
static constexpr bool is_pointer = false;
static constexpr bool is_reference = false;
};
template<typename T>
struct TypeTraits<T*> {
static constexpr bool is_pointer = true;
};
template<typename T>
struct TypeTraits<T&> {
static constexpr bool is_reference = true;
};
template<typename T>
void analyzeType(const T& value) {
if constexpr (TypeTraits<T>::is_pointer) {
std::cout << "Pointer type" << std::endl;
} else if constexpr (TypeTraits<T>::is_reference) {
std::cout << "Reference type" << std::endl;
} else {
std::cout << "Value type" << std::endl;
}
}
int main() {
int a = 5;
int* p = &a;
int& r = a;
analyzeType(a); // 输出 Value type
analyzeType(p); // 输出 Pointer type
analyzeType(r); // 输出 Reference type
return 0;
}
代码测试说明
g++ -std=c++17 -o test test.cpp && ./test
main
函数均包含测试用例。template
关键字手动实例化,减少编译开销。if constexpr
:编译期分支选择,优化生成的代码。通过以上示例和测试,可以深入理解C++模板实例化的机制和优化技巧。