8

I am writing a C++ class which represents an arithmetic type (a c++ wrapper around mpfr), and I'd like to support some functions found in <cmath> (I'll take std::sqrt as an example).

So I have the following class:

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

And I can use it this way:

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

But I cannot use it this way:

MyClass f = std::sqrt(c);

Compiler (g++ (Debian 4.7.2-5)) error is: "no matching function for call to sqrt(ns::MyClass&)".

This is normal, but it's a problem to me. I need this to be valid, because MyClass is supposed to be used into existing template functions (that I'm not supposed to modify). For example:

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

The following piece of code actually resolve my problem:

namespace std
{
  using ns::sqrt;
}

But adding things into the std namespace seems very unnatural to me. I am afraid to run into unexpected troubles later, doing this.

Is it safe ? If not, why ?

Is there a better alternative ?

doomyster
  • 123
  • 1
  • 8
  • I think it is not safe if not tested. Said this, I think if your implementation seems better why not propose it to the standard? They'll even test some data sets you may have not though of. – Claudiordgz Mar 27 '14 at 23:33
  • 2
    @Claudiordgz Presumably it's for multi-precision integers, so its purpose is orthogonal to that of `std::sqrt` which is for native types. – user1520427 Mar 27 '14 at 23:35

4 Answers4

10

Adding stuff to namespace std is prohibited by the standard. The correct way to tackle this problem is normally seen with swap (that is available in the std namespace, but that can be specialized by user-defined types to work more efficiently): when a template function needs to use e.g. sqrt, it will do

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

This way, for "regular" types it will use std::sqrt ("taken in" by the using statement), while for your type your overload will prevail due to Koenig lookup (which, incidentally, is the reason of the behavior you observed at // Apparently I don't have to specify ns::).

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • Apparently, the std::sqrt symbol is already in global namespace (no need to "using std::sqrt;"). – doomyster Mar 28 '14 at 10:03
  • 1
    `sqrt` should be in the global namespace only if you include `` instead of ``. – Matteo Italia Mar 28 '14 at 12:28
  • Well, I don't include and I still have a global symbol sqrt (made a standalone test). It seems to be the pure C sqrt function. I don't understand why it happens, but it makes your answer relevant too – doomyster Mar 28 '14 at 13:58
  • @doomyster this is a common problem of real-world C++ implementations. So common in fact, that it has been explicitly allowed since C++11 that the implementation may (or may not) declare C functions outside of namespace `std` even for `cmath`, `cstdio` and their brothers. – Ruslan Dec 31 '18 at 14:03
8

It’s not safe, because it’s not legal (§17.6.4.2.1):

The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

So you may add specialisations for your own types. You may not add overloads (or indeed anything else).

Your current code is the correct way of doing this.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • For something like `sqrt`, would it be legal to add template functions that mimic overloading? This template will match only on type `X` and therefore is 'almost' the same as an overload for `X`. E.g. (inside namespace `std`), do the following: `template auto sqrt(T x) -> typename std:: enable_if< std:: is_same::value, X > :: type { /* do square root and return */; }` – Aaron McDaid Mar 27 '14 at 23:50
  • 1
    @Aaron That’s an overload. You are not allowed to *add* function templates (= overload), you’re only allowed to specialise *existing* templates. – Konrad Rudolph Mar 28 '14 at 01:14
  • The formal proof my fears were correct. Accepted answer because you're quoting the standards. Thanks ! – doomyster Mar 28 '14 at 10:02
1

The alternative, within generic functions, is to do this:

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

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

And defining additional things into the std-namespace is not safe in general (and not strictly legal).

Mikael Persson
  • 18,174
  • 6
  • 36
  • 52
1

Not so good an idea to put it in std namespace.

Since you have your own namespace, you could import sqrt into your namespace and add specialized sqrt functions:

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

ns::sqrt(...);
Anycorn
  • 50,217
  • 42
  • 167
  • 261