在 Eigen 中实现 Clip()

bil*_*ill 0 c++ eigen eigen3

我有一些代码将一些值剪辑到以 0 为中心的范围之间,如下所示。

Eigen::VectorXd a;
Eigen::VecotrXd b;
a = a.cwiseMin(b).cwiseMax(-b);  // no temporary created here?
Run Code Online (Sandbox Code Playgroud)

我想将逻辑分解为一个函数。一种解决方案:

Eigen::VectorXd Clip(const Eigen::VectorXd& a, const Eigen::VectorXd& b);

a = Clip(a, b);
Run Code Online (Sandbox Code Playgroud)

但我认为这是低效的,因为它创建了一个额外的临时?

另一种解决方案:

void Clip(Eigen::Ref<Eigen::VectorXd> a, const Eigen::VectorXd& b) {
  a = a.cwiseMin(b).cwiseMax(-b);
}
Run Code Online (Sandbox Code Playgroud)

但这有时似乎不方便使用:

void SomeFunctionSignatureICannotChange(const Eigen::VectorXd& a, const Eigen::VectorXd& b) {
  // Eigen::VectorXd a_clipped = Clip(a, b); would be cleaner.
  Eigen::VectorXd a_clipped;
  Clip(a_clipped, b);
}
Run Code Online (Sandbox Code Playgroud)

我能想到的最佳解决方案:

template <typename DerivedV, typename DerivedB>
auto Clip(const Eigen::ArrayBase<DerivedV>& v,
          const Eigen::ArrayBase<DerivedB>& bound)
          -> decltype(v.min(bound).max(-bound)) {
  return v.min(bound).max(-bound);
}
Run Code Online (Sandbox Code Playgroud)

(我认为在这种情况下“自动”很好,而不是常见陷阱警告的那个?)

然而,代码似乎模板繁重,有点复杂。例如,后返回类型是由谷歌的风格指南鼓励在这里

仅在需要的情况下使用新的尾随返回类型形式(例如 lambdas),或者通过将类型放在函数的参数列表之后,它允许您以更易读的方式编写类型。后一种情况应该很少见;这主要是相当复杂的模板代码中的一个问题,在大多数情况下不鼓励这样做。

或者,如果我删除尾随返回类型,函数返回类型推导将开始。但谷歌风格指南似乎也不鼓励在此处的公共标题中进行函数返回类型推导

此外,仅当函数或 lambda 的范围非常窄时才使用它,因为具有推导返回类型的函数不定义抽象边界:实现就是接口。特别是,头文件中的公共函数几乎不应该推导出返回类型。

我是 Eigen 和 C++ 的新手,所以不确定我是否错过了什么。希望能从大家的意见和建议中学习。谢谢!

gga*_*ael 6

我确认a = a.cwiseMin(b).cwiseMax(-b);不会创建任何临时文件。在您的情况下,auto强烈建议使用返回类型,并且我还认为您的用例是上述规则的完全合法的例外:

  • 显式地编写返回类型将是一场噩梦并且容易出错。
  • 这是一个非常短的函数,预计会被内联。因此,预计将同时声明和定义它。因此,第二条规则并不真正适用。

在 c++14 中,您甚至可以省略冗余:

template <typename DerivedV, typename DerivedB>
auto Clip(const Eigen::ArrayBase<DerivedV>& v,
          const Eigen::ArrayBase<DerivedB>& bound)
{
  return v.min(bound).max(-bound);
}
Run Code Online (Sandbox Code Playgroud)