你有两个问题:
- 转换运行时的值(你的“类型”)成编译时确定型(带相关联的行为)。
- “统一”的可能的不同类型,以单一的(静态已知在编译时)型。
第2点就是继承与virtual
成员函数一起为:
struct Thing {
virtual void doStuff() const = 0;
virtual ~Thing() {}
};
struct AThing : Thing {
void doStuff() const override { std::cout << "A"; }
};
struct BThing : Thing {
void doStuff() const override { std::cout << "B"; }
};
1点通常是通过建立某种形式的“工厂”的机制,然后调度;基于运行时的价值解决其中一家工厂。首先,工厂:
Thing * factoryA() { return new AThing(); }
Thing * factoryB() { return new BThing(); }
Thing * factory_failure() { throw 42; }
的“调度”(或“选择合适的工厂”),可以以不同的方式来完成,这些是你的switch
声明的一个(快,但很笨拙),线性搜索通过一些容器/数组(容易,缓慢)或通过在地图中查找(对数 - 或基于哈希的地图的常量)。
我选择了(有序)的地图,但不是使用std::map
(或std::unordered_map
):我用一个std::array
避免动态内存分配(排序!):
// Our "map" is nothing more but an array of key value pairs.
template <
typename Key,
typename Value,
std::size_t Size>
using cmap = std::array<std::pair<Key,Value>, Size>;
// Long type names make code hard to read.
template <
typename First,
typename... Rest>
using cmap_from =
cmap<typename First::first_type,
typename First::second_type,
sizeof...(Rest) + 1u>;
// Helper function to avoid us having to specify the size
template <
typename First,
typename... Rest>
cmap_from<First, Rest...> make_cmap(First && first,
Rest && ... rest) {
return {std::forward<First>(first), std::forward<Rest>(rest)...};
}
使用std::lower_bound
我上执行二进制搜索此有序阵列(EHM“地图”):
// Binary search for lower bound, check for equality
template <
typename Key,
typename Value,
std::size_t Size>
Value get_from(cmap<Key,Value,Size> const & map,
Key const & key,
Value alternative) {
assert(std::is_sorted(std::begin(map), std::end(map),
[](auto const & lhs, auto const & rhs) {
return lhs.first < rhs.first; }));
auto const lower = std::lower_bound(std::begin(map), std::end(map),
key,
[](auto const & pair, auto k) {
return pair.first < k; });
if (lower->first == key) {
return lower->second;
} else {
// could also throw or whatever other failure mode
return alternative;
}
}
这样,终于,我可以用一个static const
地图获得工厂给予一定的运行时间值“类型”(或选择,因为我把它命名):
int main() {
int const choices[] = {1, 10, 100};
static auto const map =
make_cmap(std::make_pair(1, factoryA),
std::make_pair(10, factoryB));
try {
for (int choice : choices) {
std::cout << "Processing choice " << choice << ": ";
auto const factory = get_from(map, choice, factory_failure);
Thing * thing = factory();
thing->doStuff();
std::cout << std::endl;
delete thing;
}
} catch (int const & value) {
std::cout << "Caught a " << value
<< " ... wow this is evil!" << std::endl;
}
}
(Live on ideone)
的是 “地图” 大概可以做constexpr
初始化。
当然,不是原始指针(Thing *
),你应该使用托管指针(如std::unique_ptr
)。此外,如果你不想让你的处理(doStuff
)的成员函数,那么就做一个单一的“调度”(virtual
)成员调用了给定函数对象,传递自己的实例(this
)功能。使用CRTP基类,您不需要为每个类型实现该成员函数。
不能使用运行时的值来选择模板特殊化。您将以某种方式需要在运行时检查该值,并决定调用哪个函数。 –
为什么不使用虚拟功能? –
您无法摆脱读循环中心的开关。这是因为您只知道在运行时将其转换为哪种类型。 – nakiya