Overloading function templates in namespace std

Viewed 146

There was a discussion about function specialization here: Will specialization of function templates in std for program-defined types no longer be allowed in C++20?

In principal I understand, that it is better to overload instead of specialize. But how do you overload a std function template properly? The canonical answer seems to be: just overload in your custom namespace and then ADL kicks in. But this doesn't work, if fundamental types are involved. Non-working example:

#include <cmath>

namespace X {

class Y {    };

Y sqrt(Y); 

double foo(double x) { return sqrt(x); }

}

The example will only compile without the sqrt declaration for Y. One can solve the issue by overloading in namespace std instead:

#include <cmath>

namespace X {
    class Y {    };
}

namespace std { X::Y sqrt(X::Y); }

namespace X {

double foo(double x) 
{
  return sqrt(x);
}

}

This code does exactly what I want to do. However I am unsure, if this kind of overloading is permitted by the standard. At cppreference I don't find a hint toward this direction. And while this paper of Walter E. Brown proposes overloading as an alternative to specialization, I am unsure, if the above example uses it right (the paper doesn't give any examples).

2 Answers

Adding overloads of sqrt to the std namespace is not permitted.

You can however either use the fully qualified name, or give std::sqrt preference by adding using std::sqrt:

#include <cmath>

namespace X {

class Y {    };

Y sqrt(Y) { return {}; } 

double foo(double x) { 
    return std::sqrt(x); 
}

} // namespace X

template <typename T>
T bar(T x){
    using std::sqrt;
    return sqrt(x);
}

double baz(double x){
    using std::sqrt;
    return sqrt(x);
}

double moo(double x){
    return bar(x);
}

X::Y zoo(X::Y x){
    return bar(x);
}

Note that std::sqrt disables ADL, while using std::sqrt still allows ADL to kick in, as exemplified by bar which is called from moo and zoo.

Answering my own question (but wouldn't have found it without the discussion here):

#include <cmath>

namespace X {
namespace details {

class Y {    };

Y sqrt(Y); 

} // ns details

using details::Y;

double foo(double x) { return sqrt(x); }

} // ns X

Now foo can remain as it is and sqrt(Y) is possible.

Related