参数依赖名称查找的最佳实践

时间:2015-12-30 22:46:36

标签: c++ namespaces argument-dependent-lookup

今天我面临一个伤害我安心的问题。我已经在一个非常聪明且有意义的例子中恢复了我的问题,虽然没有产生错误但是没有达到预期的结果。

#include <iostream>


namespace MATH {


template <std::size_t M, std::size_t N, class T>
class Matrix
{

};

void invert(Matrix<2, 2, double>& m)
{
    std::cout << "DIM 2 : MATH version (use of the determinant)" << std::endl;
}

void invert(Matrix<3, 3, double>& m)
{
    std::cout << "DIM 3 : MATH version (use of the determinant)" << std::endl;
}


}


namespace GEOM {


template <std::size_t N>
using Matrix = MATH::Matrix<N, N, double>;// orthonormal set of vectors

template <std::size_t N>
void invert(Matrix<N>& m)
{
    std::cout << "DIM " << N << " : GEOM version (use of the transpose)" << std::endl;
}

void geom_foo_purpose(Matrix<3>& m)
{
    invert(m);
}


}


int main(int argc, char **argv)
{
    GEOM::Matrix<3> m;
    GEOM::geom_foo_purpose(m);

    return 0;
}

output : std::cout << "DIM 3 : MATH version (use of the determinant)" << std::endl;

在geom_foo_purpose定义中,对反转的调用导致不合格的id,因为请求了模板推导。所以,ADL能够说:好的,让我们看一下MATH命名空间。允许MATH :: invert优先于GEOM :: invert版本的事实,因为我认为非模板版本具有优先权是不可接受的。

例如,我想首先通过将Matrix类定义为GEOM类型来开发GEOM内容。调用GEOM :: invert。没问题。 有一天,我想在另一个命名空间MATH中推广我的Matrix类,我想:好吧,我保持相同的方法,我不打破代码。只需使用MATH :: Matrix ......我就无法理解性能开销在哪里以及为什么某些敏感措施会发生变化。

所以我实际上考虑了三个解决方案:

  • 在添加功能或使用
  • 更改时验证每个名称空间
  • 指定每次调用的命名空间
  • 在检测到时,依赖于模糊的调用编译器错误

有没有一个可行的方法来克服这个问题?

2 个答案:

答案 0 :(得分:1)

问题是,您的GEOM::Matrix只是MATH::Matrix的同义词,而不是GEOM中的新类型。这是using语句的预期效果,因此参数依赖名称查找将MATH::invert()视为最佳匹配。

如果你想解决这个问题,请用这种方式定义一个真实的GEOM::Matrix

    template <std::size_t N>
    class Matrix : public MATH::Matrix<N, N, double> {};// orthonormal set of vectors

Here an online demo

修改

您必须决定一个基本的设计问题:

  • 您希望拥有不同(但有些可互换)的Matrix类型,并从ADL中受益。在这种情况下,您可以管理两种类型,当然可以管理一些转换(从GEOM到MATH)。
  • 或者你想拥有一个单一的Matrix类型(在一个命名空间中定义),对某些参数具有特化(在同一命名空间中,即转置反转将迁移到MATH)。
Boh有他们的优点和不便。这是你的选择。我个人更喜欢第二种选择:所有矩阵特定操作都会更好地隔离。正如你所说,矩阵是一个矩阵。但显然,你已经选择了第二个选项,我的回答试图为你提供一些解决方案。

答案 1 :(得分:0)

一般建议

最佳做法是将设计为在一个命名空间中协同工作的类和函数保持为启用ADL。 (参见C ++编码标准中的第57项:将类型及其非成员函数接口保存在同一名称空间。)有时建议将不相关的类和函数保存在不同的名称空间中。 (参见同一本书中的第58项。)我发现这个限制太多,因为它很快就会创建一个巨大的命名空间丛林。

常规练习

更常见的做法是将逻辑上属于一个库的所有内容放入一个名称空间中。图书馆有责任避免内部名称冲突。库的用户通常不应该向库名称空间添加新内容。这几乎是标准库,boost,OpenCV和许多其他库的工作方式。这就是我的建议。不要试图过分。它只是伤害你的头,没有必要。

命名空间使用

这些公共库出于某些目的使用嵌套命名空间,例如boost::detail用于实现细节,这对于库的用户来说应该不是很有用。需要更多名称空间的其他示例是

  • 用于版本控制的内联命名空间和
  • 命名空间用于选择一组用户定义的文字和
  • 可能还有其他人。

修改

对您的情况的建议

如果我理解正确,您的GEOM::Matrix应该是正交矩阵。 (我只是注意到这一点,并且会建议一个更明确的名称。)这应该是一个不同的类型,这种类型必须ensure the internal invariant是正交的。使用usingtypedef只会出现别名,并且您无法保证,如果通过某些MATH::Matrix函数中断,则不会出现不变量。因此,我建议将构图作为解决问题的手段:

namespace GEOM
{

template <std::size_t N, typename T>
class OrthogonalMatrix
{
private:
    MATH::Matrix<N,N,T> mat;

public:
    // Enable use of const interface of MATH::Matrix<N,N,T>. 
    operator const MATH::Matrix<N,N,T> &() const { return mat; }

    // ... here goes the implementation ensuring 
    // orthogonality at all times.
};

} // end of namespace GEOM

另一种选择是私有继承,它允许您使用关键字MATH::Matrix选择性地公开using方法,如下所示:

namespace GEOM
{

template <std::size_t N, typename T>
class OrthogonalMatrix : private MATH::Matrix<N,N,T>;
{
public:
    // Make `MATH::Matrix<N,N,T>::transposeInPlace` publicly accessible
    using MATH::Matrix<N,N,T>::transposeInPlace;

    // Enable use of const interface of MATH::Matrix<N,N,T>. 
    operator const MATH::Matrix<N,N,T> &() const { return *this; }

    // ... here goes the implementation ensuring 
    // orthogonality at all times.
};

} // end of namespace GEOM

您不应该使用构造函数或条目访问函数来执行此操作,因为它们将使类的用户能够破坏不变量,这很容易导致很多混淆。私有部分中的公共继承加using也是错误的,因为类的用户仍然可以通过转换为MATH::Matrix<N,N,T> &来破坏不变量,然后搞乱矩阵。