我正在编写一个代表算术类型的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命名空间中添加内容对我来说似乎非常不自然。我害怕以后遇到意想不到的麻烦,这样做。
安全吗?如果没有,为什么?
有更好的选择吗?
答案 0 :(得分:7)
标准禁止向名称空间std
添加内容。解决此问题的正确方法通常见于swap
(可在std
命名空间中使用,但可以通过用户定义的类型进行专门化以更有效地工作):当模板函数需要时使用例如sqrt
,它会做
using std::sqrt;
a=sqrt(b);
这样,对于&#34;常规&#34;类型将使用std::sqrt
(&#34;在&#34; using
语句中使用),而对于您的类型,您的重载将因Koenig lookup而占优势(顺便提一下,您在// Apparently I don't have to specify ns::
处观察到的行为的原因。
答案 1 :(得分:6)
这不安全,因为它不合法(§17.6.4.2.1):
如果C ++程序向名称空间
std
或名称空间std
中的名称空间添加声明或定义,则它是未定义的,除非另有说明。只有当声明取决于用户定义的类型且专业化符合原始模板的标准库要求且未明确禁止时,程序才可以将任何标准库模板的模板特化添加到命名空间std
。
因此,您可以为自己的类型添加特化。您可能不添加重载(或其他任何事情)。
您当前的代码是执行此操作的正确方法。
答案 2 :(得分:1)
在通用函数中,替代方法是执行此操作:
template <typename T>
void func(T a)
{
using std::sqrt;
/* ... */
T c = sqrt(a);
/* ... */
}
在std命名空间中定义其他内容通常并不安全(并且不严格合法)。
答案 3 :(得分:1)
将它放在std名称空间中并不是一个好主意。
由于您拥有自己的命名空间,因此可以将sqrt导入命名空间并添加专门的sqrt
函数:
namespace ns {
using std::sqrt;
MyClass sqrt(const MyClass &)
}
ns::sqrt(...);