我可以使用模板来控制代码块并确保它不会爆炸吗?

时间:2018-06-14 19:57:17

标签: c++ c++11 templates

我正在编写一个函数,它在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]);
    }
    ...
}

但是这里有一些问题让我觉得很难:

  1. 我不知道我怎么能在这里实现我的想法。你能指点我一些例子吗?我有一种模糊的感觉,C ++ 17中的if constexpr可以帮助我。但不幸的是,我仍然在C ++ 11的土地上,并且必须留在这里一段时间。
  2. 更重要的是,我的想法值得实施吗?据我所知,C ++编译器将在编译时生成2 ^ n个函数,其中n是度量标准的数量。目前这个数字相对较小,但是函数的数量呈指数增长,而且我很确定n在某些时候会大于10。所以我的问题是我应该担心我的二进制文件会在这种情况下爆炸吗?

2 个答案:

答案 0 :(得分:1)

忘记尝试在编译时做事,并且(现在)忘记性能。一旦找到实际功能,就可以优化该部分。

在这种情况下,您[似乎试图]做的是,对于表中的每一行,评估两个预定索引上的一系列计算,并将结果推送到向量中。我不知道AB如何获得他们的价值观,所以我的解决方案不会涉及他们。

我的第一个建议是将整个事情组织成一个可以调用的函数表:

//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;
}