消除模板函数特化的歧义 - 值与引用

Bre*_*ale 1 c++ templates casting pass-by-reference c++17

这个问题需要一些上下文 -如果您感到不耐烦,请跳过换行符...我有一个Vector-3,4根据Matrix-3,4模板专业化定义的库;即,Vector<n>Matrix<n>是在 中定义的Matrix.hh,而非平凡的实现(例如,矩阵乘法、矩阵求逆)在Matrix.ccfor中有显式的专门化或实例化N = {3,4}

这种方法效果很好。理论上,应用程序可以实例化 a Matrix<100>,但无法乘法或反转矩阵,因为标头中没有可见的实现模板。仅N = {3,4}实例化于Matrix.cc

最近,我一直在添加健壮的方法来补充任何涉及内积的运算 - 包括矩阵乘法、向量的矩阵变换等。大多数 3D 变换(投影/方向)的条件相对良好,任何微小的精度误差都会受到影响。这不是问题,因为共享顶点/边会产生一致的光栅化。

有些运算必须具有数值鲁棒性。我对 GPU 在渲染时如何进行点积和矩阵运算无能为力;但我不能让控制/相机参数阻塞在有效的几何体上 - 并且内积因病态消除误差而臭名昭著,因此稳健的方法使用补偿求和、乘积、点积等。



Vector这对于以下情况的内积来说效果很好Matrix.hh

////////////////////////////////////////////////////////////////////////////////
//
// inner product:


template <int n> float
inner (const GL0::Vector<n> & v0, const GL0::Vector<n> & v1)
{
    float r = v0[0] * v1[0];

    for (int i = 1; i < n; i++)
        r += v0[i] * v1[i];

    return r; // the running sum for the inner product.
}


float
robust_inner (const GL0::Vector<3> &, const GL0::Vector<3> &);

float
robust_inner (const GL0::Vector<4> &, const GL0::Vector<4> &);


////////////////////////////////////////////////////////////////////////////////
Run Code Online (Sandbox Code Playgroud)

中的实现Matrix.cc并不简单

当添加一个稳健的矩阵乘法方法时,我处于更加可疑的境地[A]<-[A][B];也许命名并不理想:

template <int n> GL0::Matrix<n> &
operator *= (GL0::Matrix<n> & m0, const GL0::Matrix<n> & m1);

// (external instantiation)


GL0::Matrix<3> &
robust_multiply (GL0::Matrix<3> &, const GL0::Matrix<3> &);

GL0::Matrix<4> &
robust_multiply (GL0::Matrix<4> &, const GL0::Matrix<4> &);
Run Code Online (Sandbox Code Playgroud)

in 有一个实现,它依赖于朴素的内积并且不健壮 - 尽管通常对于 GL/可视化来说足够好。这些功能也在 中实现。N = {3,4}operator *=Matrix.ccrobust_multiplyMatrix.cc

当然,现在我想要Matrix乘法运算符:

template <int n> GL0::Matrix<n>
operator * (GL0::Matrix<n> m0, const GL0::Matrix<n> & m1) {
    return (m0 *= m1);
}
Run Code Online (Sandbox Code Playgroud)

让我想到有问题的定义:

inline GL0::Matrix<3>
robust_multiply (GL0::Matrix<3> m0, const GL0::Matrix<3> & m1) {
    return robust_multiply(m0, m1);
}

inline GL0::Matrix<4>
robust_multiply (GL0::Matrix<4> m0, const GL0::Matrix<4> & m1) {
    return robust_multiply(m0, m1);
}
Run Code Online (Sandbox Code Playgroud)

对 的调用robust_multiply(m0, m1)明确问:如何强制将 LHS 参数解释为引用,确保调用修改 (m0) 参数的前一个函数。显然我可以命名robust_multiply为其他名称,但我对利用类型系统更感兴趣。我觉得我遗漏了<utility>or中明显的东西<functional>。如何强制调用正确的函数?


(抱歉字数问题——我在写的时候试图澄清我自己的想法)

Yak*_*ont 5

你叫robust_multiply错了。

*=*根本不同的操作。它们是相关的,但不是相同的操作 - 不同的动词。

当你对不同的名词进行相同的操作时,应该使用重载。

如果你这样做了,那么你的问题几乎肯定会消失。合理的重载很容易编写。

在您的情况下,您希望根据其 l/r 值类别在写入或不写入参数之间进行更改。这会导致歧义问题。

我的意思是,您的问题有解决方法 - 使用std::ref或指针,例如,&or&&const&- 但它们在这里是补丁。

在编程中命名它是很困难的。这是一个你应该做的困难的例子。

...

现在你可以做的一件事就是祝福这些论点。

template<class T>
struct robust{
  T t;
  explicit operator T&()&{return t;}
  explicit operator T()&&{
    return std::forward<T>(t);
  }
  // also get() methods
  explicit robust(T&&tin):
    t(std::forward<T>(tin))
  {}
};
Run Code Online (Sandbox Code Playgroud)

然后覆盖*=*以获得稳健的包装矩阵。

robust{a}*=b;
Run Code Online (Sandbox Code Playgroud)

(lhs 必须足够强大才能减少过载计数)。

现在动词已经清楚了,我只是修饰了一下名词。

但这只是一个想法,并没有经过使用测试。