问题描述
我在我的一个项目中有很多自定义数据类型,它们都有一个共同的基类。
我的数据(来自数据库)有一个数据类型,用基类的枚举来区分。我的架构允许一个特定的数据类型专用于一个派生类或者它可以由基类处理。
I have a lot of custom datatypes in one of my projects which all share a common base class.My data (coming from a database) has a datatype which is distinguished by an enum of the base class. My architecture allows a specific datatype to be specialized with a derived class or it can be handled by the base class.
当我构造一个我的特定数据类型通常调用构造函数直接:
When I construct one my specific datatypes i normally call the constructor directly:
Special_Type_X a = Special_Type_X("34.34:fdfh-78");
a.getFoo();
有一些模板魔法,它允许像这样构造:
There is some template magic which also allows constructing it like this:
Type_Helper<Base_Type::special_type_x>::Type a = Base_Type::construct<Base_Type::special_type_x>("34.34:fdfh-78");
a.getFoo();
对于枚举类型的某些值,可能没有特殊化,因此
For some values of the type enum there might be no specialization so
Type_Helper<Base_Type::non_specialized_type_1>::Type == Base_Type
当从数据库提取数据时,数据类型在编译时是未知的,因此有一个第三种方法来构造数据类型(来自QVariant):
When im fetching data from the database the datatype isn't known at compile time so there's a third way to construct the datatypes (from a QVariant):
Base_Type a = Base_Type::construct(Base_type::whatever,"12.23@34io{3,3}");
但是当然我想调用正确的构造函数,所以执行该方法像:
But of course I want the correct constructor to be called, so the implementation of that method used to look like:
switch(t) {
case Base_Type::special_type_x:
return Base_Type::construct<Base_Type::special_type_x>(var);
case Base_Type::non_specialized_type_1:
return Base_Type::construct<Base_Type::non_specialized_type_1>(var);
case Base_Type::whatever:
return Base_Type::construct<Base_Type::whatever>(var);
//.....
}
此代码是重复的因为基类可以处理新类型(添加到枚举),我想出了以下解决方案:
This code is repetitive and since the base class can handle new types (added to the enum) as well, i came up with the following solution:
//Helper Template Method
template <Base_Type::type_enum bt_itr>
Base_Type construct_switch(const Base_Type::type_enum& bt, const QVariant& v)
{
if(bt_itr==bt)
return Base_Type::construct<bt_itr>(v);
return construct_switch<(Base_Type::type_enum)(bt_itr+1)>(bt,v);
}
//Specialization for the last available (dummy type): num_types
template <>
Base_Type construct_switch<Base_Type::num_types>(const Base_Type::type_enum& bt, const QVariant&)
{
qWarning() << "Type"<<bt<<"could not be constructed";
return Base_Type(); //creates an invalid Custom Type
}
return construct_switch<(Base_Type::type_enum)0>(t,var);
此解决方案按预期工作。
编译的代码是不同的。虽然原始的switch语句具有O(1)的复杂度,新的Approch导致O(n)复杂度。生成的代码递归调用我的帮助方法,直到它找到正确的条目。
为什么编译器不能正确优化?有没有更好的方法来解决这个问题?
This solution works as expected.The compiled code is however different. While the original switch statement had a complexity of O(1) the new approch results in a O(n) complexity. The generated code recursivly calls my helper method until it finds the correct entry.Why can't the compiler optimize this properly? Are there any better ways to solve this?
类似的问题:
我应该提到,我想避免C ++ 11和C ++ 14和坚持C ++ 03。
I should mention that I would like to avoid C++11 and C++14 and stick to C++03.
推荐答案
这是魔术开关问题 - 如何获取一个(范围)运行时间值并将其转换为编译时常数。
This is the magic switch problem -- how to take a (range of) run time values and turn it into a compile time constant.
从C ++ 1y开始已更改的样板:
Start with C++1y-replaced boilerplate:
template<unsigned...> struct indexes {typedef indexes type;};
template<unsigned max, unsigned... is> struct make_indexes: make_indexes<max-1, max-1, is...> {};
template<unsigned... is> struct make_indexes<0, is...>:indexes<is...> {};
template<unsigned max> using make_indexes_t = typename make_indexes<max>::type;
现在我们可以轻松地创建一个从0到n-1的无符号整数的编译时序列。 make_indexes_t< 50>
扩展为索引 ...
,48,49>
。 C ++ 1y版本在对数递归步骤中是这样,上面是在线性(在编译时 - 没有在运行时完成),但是你有超过几百种类型?
Now we can create a compile-time sequence of unsigned integers from 0 to n-1 easily. make_indexes_t<50>
expands to indexes<0,1,2,3,
... ,48, 49>
. The C++1y version does so in logarithmic recursion steps, the above does it in linear (at compile time -- nothing is done at run time), but do you have more than a few 100 types?
接下来,我们构建一个回调数组。由于我讨厌C遗留的函数指针语法,我会抛出一些无意义的样板来隐藏它:
Next, we build an array of callbacks. As I hate C legacy function pointer syntax, I'll throw in some pointless boilerplate to hide it:
template<typename T> using type = T; // pointless boilerplate
template<unsigned... Is>
Base_Type construct_runtime_helper( indexes<Is...>, Base_Type::type_enum e, QVariant const& v ) {
// array of pointers to functions: (note static, so created once)
static type< Base_Type(const QVariant&) >* constructor_array[] = {
(&Base_Type::construct<Is>)...
};
// find the eth entry, and call it:
return constructor_array[ unsigned(e) ](v);
}
Base_Type construct_runtime_helper( Base_Type::type_enum e, QVariant const& v ) {
return construct_runtime_helper( make_indexes_t< Base_Type::num_types >(), e, v );
}
bob是你的叔叔。 O(1)数组查找(使用O(n)设置,这在理论上可以在可执行文件启动之前完成)
and bob is your uncle. O(1) array lookup (with an O(n) setup, which in theory could be done prior to your executable launching)
这篇关于优化交换机的模板更换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!