1

Wrote my own numeric class. Overloaded std::numeric_limit::max() fine. However, I'm trying to overload std::abs() but the compiler doesn't like my attempt. I've tried this:

namespace std 
{
    template<uint8_t T> 
    MyType<T> abs(const MyType<T>& mt)
    {
        MyType<T> copy = mt;
        copy.val = std::abs(md.val);
        return copy;
    }
}

However, the compiler isn't finding the overload:

error: no matching function for call to ‘abs(const MyType<10>&)’
/usr/include/c++/11/bits/std_abs.h:56:3: note: candidate: ‘long int std::abs(long int)’
   56 |   abs(long __i) { return __builtin_labs(__i); }
      |   ^~~
/usr/include/c++/11/bits/std_abs.h:56:12: note:   no known conversion for argument 1 from ‘const MyType<10>’ to ‘long int’
   56 |   abs(long __i) { return __builtin_labs(__i); }
      |       ~~~~~^~~
/usr/include/c++/11/bits/std_abs.h:61:3: note: candidate: ‘long long int std::abs(long long int)’
   61 |   abs(long long __x) { return __builtin_llabs (__x); }
      |   ^~~
/usr/include/c++/11/bits/std_abs.h:61:17: note:   no known conversion for argument 1 from ‘const MyType<10>’ to ‘long long int’
   61 |   abs(long long __x) { return __builtin_llabs (__x); }
      |       ~~~~~~~~~~^~~
/usr/include/c++/11/bits/std_abs.h:71:3: note: candidate: ‘constexpr double std::abs(double)’
   71 |   abs(double __x)
      |   ^~~
/usr/include/c++/11/bits/std_abs.h:71:14: note:   no known conversion for argument 1 from ‘const MyType<10>’ to ‘double’
   71 |   abs(double __x)
      |       ~~~~~~~^~~
/usr/include/c++/11/bits/std_abs.h:75:3: note: candidate: ‘constexpr float std::abs(float)’
   75 |   abs(float __x)
      |   ^~~
/usr/include/c++/11/bits/std_abs.h:75:13: note:   no known conversion for argument 1 from ‘const MyType<10>’ to ‘float’
   75 |   abs(float __x)
      |       ~~~~~~^~~
/usr/include/c++/11/bits/std_abs.h:79:3: note: candidate: ‘constexpr long double std::abs(long double)’
   79 |   abs(long double __x)
      |   ^~~
/usr/include/c++/11/bits/std_abs.h:79:19: note:   no known conversion for argument 1 from ‘const MyType<10>’ to ‘long double’
   79 |   abs(long double __x)

Code calling it:

MyType<10> mt;
MyType<10> abs = std::abs(mt);
intrigued_66
  • 16,082
  • 51
  • 118
  • 189
  • Related: [C++ When is it OK to extend the `std` namespace?](https://stackoverflow.com/questions/41062294/c-when-is-it-ok-to-extend-the-std-namespace) and [Should you overload swap in the std namespace?](https://stackoverflow.com/questions/14402990/should-you-overload-swap-in-the-std-namespace) – Jason Oct 12 '22 at 12:38
  • Dupe: [How to add std::swap for my template class?](https://stackoverflow.com/questions/6414196/how-to-add-stdswap-for-my-template-class). Even more close dupe: [Overloading global swap for user-defined type](https://stackoverflow.com/questions/2223245/overloading-global-swap-for-user-defined-type) – Jason Oct 12 '22 at 12:39

2 Answers2

6

You are not allowed to add overloads to the std namespace. You are allowed to specialize the functions until C++20, but this wont help as your type is templated and partial template specialization of functions is not allowed.

Typically what you do is in your code you import the function you want to use, and then do an unqualified call to said function like

template <typename T>
auto some_func(const T& foo)
{
    using std::abs;
    return abs(foo);
}

and now if T is a type std::abs can handle then it will be used. If it is not, but it is a type that has it's own abs function either declared in the global space or in that types own namespace then it will be found using the unqualified lookup rules.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • MyType can represent a double. Sometimes another class templates on MyType and sometimes it uses double. The templated class calls `std::abs()` – intrigued_66 Oct 12 '22 at 12:38
  • 1
    @mezamorphic Not exactly sure what you are trying to say there. If this wont work for you, can you update your Q with a more detailed example? – NathanOliver Oct 12 '22 at 12:41
  • I have another class, let's say it's a container. It is templated on either `MyType` or `double`. Some logic within the container uses `std::abs(double)` so I now need to make it work with `std::abs(MyType)`. I'm not sure how your solution would still work with `double`? – intrigued_66 Oct 12 '22 at 12:48
  • @mezamorphic You can't without changing the `std::abs` call to use the pattern from the answer (directly or indirectly). – user17732522 Oct 12 '22 at 12:50
  • 1
    @mezamorphic You need to change your container to call `abs` like `some_func` does in my example. If you can't do that, then there is not much you can do. – NathanOliver Oct 12 '22 at 12:50
  • @NathanOliver ah I gave the other person the acceptance but I'll upvote your comments and answer. Thank you – intrigued_66 Oct 12 '22 at 12:52
  • @mezamorphic No worries. Glad you got it figured out. – NathanOliver Oct 12 '22 at 12:53
4

Adding function or function template declarations into the std namespace is not allowed.

You should put the overloads into the namespace where your custom type is declared and then call the function with an unqualified name, so that argument-dependent lookup can find your overload.

If you are not sure at the call site whether std::abs or your overload should be called, then do using std::abs; in the calling scope before the call.

Customizing abs like that is however not intended like it is for e.g. swap and you cannot assume that a library user will call it in the above form.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • The reason I need to overload std is because my type can replace a 'double', which calls `std::abs()` with no problems. I can't write any class-specific logic because then `double` won't have it. – intrigued_66 Oct 12 '22 at 12:37
  • 2
    @mezamorphic If you do `using std::abs;` at the call site before calling `abs` with unqualified name your overload will be chosen if it is a better fit than the ones from the standard library, but otherwise the standard library overloads will be chosen. You can however not do anything about a user calling `std::abs` with a qualified name. – user17732522 Oct 12 '22 at 12:38
  • Do you mean it will call my overloaded std function? Or I need to move my `abs()` inside `MyType` as a class method? – intrigued_66 Oct 12 '22 at 12:40
  • 2
    @mezamorphic You should move the `abs` function you are showing in the question as is into the same scope where the class is defined, but not inside the class. Then the `using std::abs; abs(...);` pattern will pick up your `abs` function or the standard one as suitable (from anywhere). – user17732522 Oct 12 '22 at 12:40
  • Ah I see what you mean. Awesome, I just tried and it works. Thank you so much for that little trick. – intrigued_66 Oct 12 '22 at 12:51