出于学习目的,我正在尝试实施幼稚的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)...);
}
};
使用可变参数模板来扩展条件。
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)...);
}
使用部分专业化来匹配不同的情况。
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.不清楚的语义。
那么,如何正确,更好地解决问题?(通过一系列条件表达式产生不同的标签)
答案 0 :(得分:1)
这些解决方案似乎有些复杂。另一种解决方案是使用带有SFINAE限制的重载辅助函数,甚至可能重载newElement
本身。好处之一是可以在实现旁边看到该条件。
#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)...);
}