3

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).

krzikalla
  • 73
  • 3
  • What's wrong with calling `std::sqrt(x);` or `using std::sqrt` inside `foo`? – n. m. could be an AI May 31 '21 at 11:24
  • Regarding `std::sqrt(x);` see below. And writing `using std::sqrt` (same for sin, exp aso.) all over the places in your code just to reactivate standard functions for fundamental types feels rather like a language flaw. – krzikalla May 31 '21 at 12:16
  • 1
    `#include ` does not have to bring `sqrt` et al into global namespace. Your implementation does that for one reason or another, but that's not what the language guarantees. Normally you should be calling `std::sqrt` everywhere, or saying `using std::sqrt`, regardless of whether you define your own `sqrt`. – n. m. could be an AI May 31 '21 at 12:26
  • Regarding "see below", I came from below hoping to get a hint here what the problem actually is. You seem the be expecting `std::sqrt` to be something that it isnt. You want something else so you need to do a tiny bit extra. Its not a language flaw, i'd rather consider it as a langauge flaw if `std::sqrt` could call whatever, but is not guaranteed to be `std::sqrt`. – 463035818_is_not_an_ai May 31 '21 at 12:28
  • You might be interested by [P1292R0 Customization Point Functions](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1292r0.html). – Jarod42 May 31 '21 at 13:26
  • The original problem is a large code base, which uses math functions all over the place (and always without `std::`). Now all that code broke, after a class like `X::Y` was introduced (note, that the class is unrelated to `foo`). BTW; I found a nice solution by myself: put Y and its related functions (e.g. sqrt) in a sub-namespace and then publish Y (but not the functions) to the enclosing namespace. @Jarod42: thanks for the link. It seems to tackle also my issue. – krzikalla May 31 '21 at 14:07

2 Answers2

2

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.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • This was exactly the answer I was afraid of. As you've noted, full qualification doesn't work with templates. And writing using std::sqrt becomes painful in numerical codes. And all theses problems only, because fundamental data types don't add std to their ADL namespaces. BTW, a while ago I asked a related question: https://stackoverflow.com/questions/64785956/ – krzikalla May 31 '21 at 12:03
  • 1
    @krzikalla "As you've noted, full qualification doesn't work with templates. " sry I dont understand what you mean by that. – 463035818_is_not_an_ai May 31 '21 at 12:05
  • Sorry, the commenting UI is difficult to master. Anyway, thanks for the answer. It looks like I have to use `using std::sqrt;` in theory. In practice I have to violate the std. – krzikalla May 31 '21 at 12:12
  • @krzikalla I am a bit lost on why you see a problem. – 463035818_is_not_an_ai May 31 '21 at 12:16
  • @krzikalla if "writing using std::sqrt [...] all over the place" is what worries you then you can write a wrapper `my_sqrt` that can be called without such quirks. – 463035818_is_not_an_ai May 31 '21 at 12:19
0

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.

krzikalla
  • 73
  • 3