c++ - 通过模板参数“T”的大小在编译时解析不同的策略
问题描述
我正在尝试实现一个简单的 GC 系统以用于学习目的。基本上,它将newElement
为用户提供一个“新建”对象的界面。
我要解决的策略是根据对象类型通过不同的策略分配对象(小对象的池/大对象的malloc ...),例如:
T* newElement(Args&&... args)
{
if(sizeof(T) < 4){
// strategy1
return newImpl1(std::forward<Args>(args)...);
} else if(4 <= sizeof(T) < 16){
// strategy2
return newImpl2(std::forward<Args>(args)...);
} else{
// strategy3
return newImpl3(std::forward<Args>(args)...);
}
}
我认为这个成本可以在编译时而不是运行时,因为sizeof(T)
可以在编译时评估。我知道在 C++17 中,我们有constexpr if
处理这种情况的特性。但是,我正在处理 VS 2015,它只支持 C++11 和 C++14。所以,我认为这个过程是两个不同的阶段:
“新”应该接受不同类型的标签(按类型)来解决不同的策略
“调度”应该接受 T 作为输入,并有办法输出正确的标签(按类型)
通常,阶段 2 的目的是通过一系列条件表达式输出不同类型的标签(无论是值还是类型)。
我想到了两种解决方案。
enum class Strategy {
small, middle, big
};
constexpr size_t SmallMiddleThreshold = 4;
constexpr size_t MiddleBigThreshold = 8;
template
<Strategy s=Strategy::small>
struct newImpl {
template
<typename T, typename... Args>
static T* apply(Args&&... args)
{
cout << "small!" << endl;
return new T(std::forward<Args>(args)...);
}
};
template
<>
struct newImpl<Strategy::middle> {
template
<typename T, typename... Args>
static T* apply(Args&&... args)
{
cout << "middle!" << endl;
return new T(std::forward<Args>(args)...);
}
};
template
<>
struct newImpl<Strategy::big> {
template
<typename T, typename... Args>
static T* apply(Args&&... args)
{
cout << "big!" << endl;
return new T(std::forward<Args>(args)...);
}
};
解决方案 1
使用可变参数模板来扩展条件。
template
<bool Condition1=true, bool... Conditions>
struct SizeDispatcher1 {
constexpr static Strategy value = Strategy::small;
};
template
<bool... Conditions>
struct SizeDispatcher1<false, Conditions...> {
constexpr static Strategy value = SizeDispatcher2<Conditions...>::value;
};
template
<bool Condition2 = true, bool... Conditions>
struct SizeDispatcher2 {
constexpr static Strategy value = Strategy::middle;
};
template
<bool... Conditions>
struct SizeDispatcher2<false, Conditions...> {
constexpr static Strategy value = SizeDispatcher3<Conditions...>::value;
};
template
<bool Condition3 = true, bool... Conditions>
struct SizeDispatcher3 {
constexpr static Strategy value = Strategy::big;
};
template
<typename T>
struct SizeDispatcher {
constexpr static Strategy value =
SizeDispatcher1<
sizeof(T) < SmallMiddleThreshold,
SmallMiddleThreshold <= sizeof(T) && sizeof(T) < MiddleBigThreshold,
MiddleBigThreshold <= sizeof(T)
>::value;
};
template
<typename T, typename... Args>
T* newElement(Args&&... args)
{
return newImpl<SizeDispatcher<T>::value>::apply<T>(std::forward<Args>(args)...);
}
解决方案 2
使用偏特化来匹配不同的情况。
template
<bool Condition1=true, bool Condition2=false, bool Condition3=false>
struct SizeDispatcherImpl {
constexpr static Strategy value = Strategy::small;
};
template
<>
struct SizeDispatcherImpl<false, true, false> {
constexpr static Strategy value = Strategy::middle;
};
template
<>
struct SizeDispatcherImpl<false, false, true> {
constexpr static Strategy value = Strategy::big;
};
template
<typename T>
struct SizeDispatcher {
constexpr static Strategy value =
SizeDispatcherImpl<
sizeof(T) < SmallMiddleThreshold,
SmallMiddleThreshold <= sizeof(T) && sizeof(T) < MiddleBigThreshold,
MiddleBigThreshold <= sizeof(T)
>::value;
};
但是,我对上述代码有一些疑问。
首先,它能否正确满足我的要求?也就是在编译时解决不同的策略?
其次,这两种解决方案都至少有以下缺点: 1.“调度程序”与条件表达式(格式、序列...)紧密耦合,这绝对不是编码的好习惯。2.语义不清晰。
那么,如果可能的话,如何正确更好地解决这个问题?(通过一系列条件表达式产生不同的标签)
解决方案
这些解决方案看起来确实有点复杂。另一种解决方案是使用重载的辅助函数,甚至可能重载newElement
自身,但有 SFINAE 限制。一个好处是可以在实现旁边看到条件。
#include <type_traits>
#include <iostream>
template <typename T, typename... Args, std::enable_if_t<(sizeof(T) < 4)>* = nullptr>
T* newElement(Args&& ... args) {
std::cout << "small!" << std::endl;
return new T(std::forward<Args>(args)...);
}
template <typename T, typename... Args,
std::enable_if_t<(sizeof(T) >= 4 && sizeof(T) < 16)>* = nullptr>
T* newElement(Args&& ... args) {
std::cout << "middle!" << std::endl;
return new T(std::forward<Args>(args)...);
}
template <typename T, typename... Args, std::enable_if_t<(sizeof(T) >= 16)>* = nullptr>
T* newElement(Args&& ... args) {
std::cout << "big!" << std::endl;
return new T(std::forward<Args>(args)...);
}
推荐阅读
- wget - 如何修复 Wget 参数名称“C”不明确?
- ios - Swift 委托和协议
- node.js - 如何使用 sapper 将其他文件导入 server.js?
- javascript - Razorpay nodejs express 集成问题
- swift - 如何创建一个“对齐”的字节数组并从中读取?
- informatica - 我想在 Informatica 的表达式转换器中使用条件表达式
- laravel - laravel 本地化使用 mcamara 包
- vue.js - 如何在 vuejs 的母版页中打开新链接
- java - Swagger 是否与 Struts 一起使用来记录 REST api?
- php - Laravel 网站未连接(数据库连接失败)