我正在编写一个函数,它在C ++中的大型表数据列表上进行一些计算。该功能应该包含"指令列表"指定要计算的内容。
例如,表格看起来像这样
| A | B | C | |---|---|---| | 1 | 2 | 4 | | 2 | 3 | 5 | | 3 | 5 | 6 | |...........|
我正在尝试构建一个看起来像这样的函数
std::vector<int> compute(const Table& input, const std::vector<MetricsEnum>& metrics)
{
std::vector<int> result;
result.reserve(some heuristic number I put in);
for(const auto& row: input)
{
if(std::find(metrics.begin(), metrics.end(), Metrics1) != metrics.end())
{
result.push_back(row[A] + row[B]);
}
if(std::find(metrics.begin(), metrics.end(), Metrics2) != metrics.end())
{
result.push_back(row[A] - row[B]);
}
// this list goes for about 5 or 6 metrics for now, but later I plan to add more
}
}
所以我遇到的问题是,显然输入有很多行,而在循环中做if语句对我来说更像是一种奢侈。我希望能够使用模板在编译时生成一堆函数,并根据运行时我想要的指标选择其中一个函数。与...有关的东西:
template <bool metrics1, bool metrics2 ...>
std::vector compute(const Table& input>
{
...
if(metrics1)
{
result.push_back(row[A] + row[B]);
}
if(metrics2)
{
result.push_back(row[A] - row[B]);
}
...
}
但是这里有一些问题让我觉得很难:
if constexpr
可以帮助我。但不幸的是,我仍然在C ++ 11的土地上,并且必须留在这里一段时间。 答案 0 :(得分:1)
忘记尝试在编译时做事,并且(现在)忘记性能。一旦找到实际功能,就可以优化该部分。
在这种情况下,您[似乎试图]做的是,对于表中的每一行,评估两个预定索引上的一系列计算,并将结果推送到向量中。我不知道A
或B
如何获得他们的价值观,所以我的解决方案不会涉及他们。
我的第一个建议是将整个事情组织成一个可以调用的函数表:
//Replace all instances of 'int' with whatever type you're using
std::vector<int> compute(const Table& input, const std::vector<MetricsEnum>& metrics)
{
typedef int (*func_ptr_type)(int,int);
//I'm assuming MetricsEnum is a literal enum type, convertible to an integer.
static const std::array<func_ptr_type, 6> functions{
+[](int a, int b) {return a + b;},
+[](int a, int b) {return a - b;},
+[](int a, int b) {return a * b;},
+[](int a, int b) {return a / b;},
+[](int a, int b) {return a << b;},
+[](int a, int b) {return a >> b;}
//Add more and increase the size of the array, as needed
};
std::vector<int> result;
//Don't do this; let the compiler and allocator do their jobs
//result.reserver(some heuristic number I put in);
for(const auto& row: input)
{
for(MetricsEnum metricsEnum : metrics) {
result.emplace_back(functions.at(size_t(metrics))(row[A], row[B]));
}
}
return result;
}
在这种形式下,可以更容易地看到代码本来要做的事情,并且我们也发现保持整个事情的组织要容易得多。
下一步是完全消除数组,并使函数成为MetricsEnum
类型行为的核心部分,无论是什么。
template<typename T>
class MetricsEnum {
public:
enum class Type {
add, subtract, multiply, divide, shift_left, shift_right
};
constexpr MetricsEnum(Type type) : type(type) {}
constexpr T operator()(T a, T b) const {
switch(type) {
case Type::add: return a + b;
case Type::subtract: return a - b;
case Type::multiply: return a * b;
case Type::divide: return a / b;
case Type::shift_left: return a << b;
case Type::shift_right: return a >> b;
default: return {};
}
}
private:
Type type;
};
std::vector<int> compute(const Table& input, const std::vector<MetricsEnum<int>>& metrics)
{
std::vector<int> result;
for(const auto& row: input)
{
for(auto const& metricsEnum : metrics) {
result.emplace_back(metricsEnum(row[A], row[B]));
}
}
return result;
}
有许多其他方法可以解决这个问题(想到多态性......);这对我来说是最直观的。
答案 1 :(得分:0)
更重要的是,我的想法值得实施吗?据我所知,C ++编译器将在编译时生成2 ^ n个函数,其中n是度量标准的数量。
正确。
您可能会做的是按类型计算指标并将结果放在正确的位置,例如:
template <typename F>
void loop(const Table& input, std::size_t blockSize,
std::size_t& offset, std::vector<int>& result,
F f)
{
for (std::size_t i = 0; i != input.size(); ++i) {
const auto& row = input[i];
result[blockSize * i + offset] = f(row);
}
++offset;
}
std::vector<int> compute(const Table& input, const std::vector<MetricsEnum>& metrics)
{
std::vector<int> result(input.size() * metrics.size());
std::size_t offset = 0;
if (std::find(metrics.begin(), metrics.end(), Metrics1) != metrics.end()) {
loop(input, metrics.size(), offset, result,
[](const Row& row) { return row[A] + row[B]; });
}
if (std::find(metrics.begin(), metrics.end(), Metrics2) != metrics.end()) {
loop(input, metrics.size(), offset, result,
[](const Row& row) { return row[A] - row[B]; });
}
// this list goes for about 5 or 6 metrics for now, but later I plan to add more
return result;
}