7

我正在编写一个表示算术类型的 C++ 类(围绕mpfr的 c++ 包装器),并且我想支持 <cmath> 中的一些函数(我将以 std::sqrt 为例)。

所以我有以下课程:

namespace ns
{
  class MyClass
  {
      /* ... */
      public:
      friend MyClass sqrt(const MyClass& mc);
  };
}

我可以这样使用它:

MyClass c;
/* ... */
MyClass d = ns::sqrt(c);
MyClass e = sqrt(c); // Apparently I don't have to specify ns::

但我不能这样使用它:

MyClass f = std::sqrt(c);

编译器 (g++ (Debian 4.7.2-5)) 错误是:“没有匹配函数调用 sqrt(ns::MyClass&)”。

这很正常,但对我来说是个问题。我需要这是有效的,因为 MyClass 应该用于现有的模板函数(我不应该修改)。例如:

template <typename T>
void func(T a)
{
    /* ... */
    T c = std::sqrt(a);
    /* ... */
}
int main()
{
    func<float>(3);
    func<MyClass>(MyClass(3));
    /* ... */
}

以下代码实际上解决了我的问题:

namespace std
{
  using ns::sqrt;
}

但是在 std 命名空间中添加东西对我来说似乎很不自然。我怕以后会遇到意想不到的麻烦,这样做。

安全吗?如果不是,为什么?

有更好的选择吗?

4

4 回答 4

9

std标准禁止向命名空间添加东西。解决这个问题的正确方法通常swap是(在std命名空间中可用,但可以由用户定义的类型专门化以更有效地工作):当模板函数需要使用 egsqrt时,它会做

using std::sqrt;
a=sqrt(b);

这样,对于“常规”类型,它将使用std::sqrt(由using语句“吸收”),而对于您的类型,由于Koenig 查找,您的重载将占上风(顺便说一句,这是您在 观察到的行为的原因// Apparently I don't have to specify ns::)。

于 2014-03-27T23:35:26.840 回答
7

它不安全,因为它不合法(§17.6.4.2.1):

std如果 C++ 程序将声明或定义添加到命名空间或命名空间内的命名空间,则 C++ 程序的行为是未定义的,std除非另有说明。std只有当声明依赖于用户定义的类型并且特化满足原始模板的标准库要求并且没有明确禁止时,程序才能将任何标准库模板的模板特化添加到命名空间。

因此,您可以为自己的类型添加专业化。您可能不会添加重载(或其他任何东西)。

您当前的代码是这样做的正确方法。

于 2014-03-27T23:34:35.910 回答
1

在泛型函数中,替代方法是这样做:

template <typename T>
void func(T a)
{
    using std::sqrt;

    /* ... */
    T c = sqrt(a);
    /* ... */
}

并且在标准命名空间中定义额外的东西通常是不安全的(并且不是严格合法的)。

于 2014-03-27T23:35:36.043 回答
1

将它放在 std 命名空间中并不是一个好主意。

由于你有自己的命名空间,你可以将 sqrt 导入你的命名空间并添加专门的sqrt函数:

namespace ns {
  using std::sqrt;
  MyClass sqrt(const MyClass &)
}

ns::sqrt(...);
于 2014-03-27T23:36:21.273 回答