【问题标题】:How to eliminate 'Only index into arrays using constant expressions' warning?如何消除“仅使用常量表达式对数组进行索引”警告?
【发布时间】:2017-11-18 13:40:47
【问题描述】:

我写了一个kd-tree模板,它的参数是自然数K。

作为模板的一部分,我编写了以下函数来计算两点之间的距离(kd_point 是 std::array 的别名)

template <unsigned K>
float kd_tree<K>::DistanceSq(const kd_point &P, const kd_point &Q)
{
    float Sum = 0;

    for (unsigned i = 0; i < K; i++)
        Sum += (P[i] - Q[i]) * (P[i] - Q[i]);

    return Sum;
}

我已打开“启用 C++ 核心检查(发布)”,它给了我上述警告。是否有正确的方法编写此例程以消除警告?

【问题讨论】:

  • 基本上这意味着不要使用原始 for 循环,使用范围 for 循环。我不知道它如何适用于您的情况,但也许其他人可以阐明。
  • 我知道如何编写一个范围 for 循环来访问任一 kd 点的每个元素。如何编写一个循环范围以访问两者以计算差异?

标签: c++ guideline-support-library


【解决方案1】:

由于您在 cmets 中提到您的 kd_point 支持基于范围的迭代(所以我假设可以返回迭代器),您可以在没有原始循环的情况下重新编写函数。改用标准库中的命名算法:

template <unsigned K>
float kd_tree<K>::DistanceSq(const kd_point &P, const kd_point &Q)
{
  return std::inner_product(
    begin(P), end(P), begin(Q), 0.0f, std::plus<float>{},
    [](float pi, float qi) {
      return (pi - qi)*(pi - qi);
    }
  );
}

标准库当然不会受到警告的影响。如果(在这种情况下)用命名操作替换原始循环的边际好处对您没有吸引力,请考虑如果您使用启用 C++17 的编译器返回此代码,您将能够几乎毫不费力地并行化它:

template <unsigned K>
float kd_tree<K>::DistanceSq(const kd_point &P, const kd_point &Q)
{
  return std::transform_reduce(std::execution::par, // Parallel execution enabled 
    begin(P), end(P), begin(Q), 0.0f, std::plus<float>{},
    [](float pi, float qi) {
      return (pi - qi)*(pi - qi);
    }
  );
}

【讨论】:

  • 只有我一个人,还是每个版本的 c++ 可读性都变差了?
  • @Rotem - 标准库从一开始就有std::inner_product。相信我,一个 lambda 使这比它本来的可读性要强得多。代替原始循环的命名操作更容易“获取”。我的意思是,是的,有相当多的样板,但现在有点明显,它做了某种内积运算,不是吗?
  • 因为 kd 点是一个 std::array,它有范围迭代器并且代码可以工作。谢谢!我明白这背后的逻辑,但有点同意 Rotem - 这种代码需要习惯。
  • @UriRaz - 当然同意你的观点。这个特定的代码并没有做太多的例子,但是一旦你开始将算法组合在一起,原始循环很快就会被抛在后面,尽管它很简单。
【解决方案2】:

Answer by StoryTeller 可能是解决这一特定任务的最合适的 C++ 方式。

我想补充一点,一般来说,如果你想迭代不是超过一个,而是同时超过两个序列,你可以使用“boost::range::for_each的秘密重载”,接受两个范围:

#include <boost/range/algorithm_ext/for_each.hpp>

template <unsigned K>
float kd_tree<K>::DistanceSq(const kd_point &P, const kd_point &Q)
{
    float Sum = 0;

    boost::range::for_each(P, Q, [&Sum](float p, float q) 
    { 
      Sum += (p - q) * (p - q);
    });        

    return Sum;
}

请注意,与标准算法类似,此算法仅是标头,不会为您的代码带来任何库依赖。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-31
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 2015-01-19
    • 1970-01-01
    相关资源
    最近更新 更多