我正试图实现一个简单的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。语义不清。
那么,如果可能,如何正确地更好地解决这个问题呢?(通过一系列条件表达式产生不同的标签)