Consider the following example for the calculation of the absolute difference between two values.
template<class T>
auto abs_diff(const T _a, const T _b) {
return (_a > _b) ? (_a - _b) : (_b - _a);
}
For some build-in types std::abs
provides superior performance, so if std::abs(T x)
exists I want to use it (let's assume we are not interested in the different under-/overflow behavior in case of integers). To this end I tried to add the following constrained template function
template<class T> requires requires (T x) { std::abs(x); }
auto abs_diff(const T _a, const T _b) {
return std::abs(_a - _b);
}
Now all types for which std::abs
exists will use this specialized version. However also all types which are implicitly convertible to such a type will use it, which is undesirable. So my question is: is there a way to require the existence of a function with exact signature in a concept (i.e. the existence of std::abs(T x)
and not only the fact that std::abs(x)
compiles).
Note: The above example is mostly for illustration and could, at least for my application, be fixed by constraining the return type using requires (T x) { { std::abs(x) } -> std::same_as<T>; }
. However I am interested in a general solution for this kind of problem.
We can test instead if the overloaded function address expression
&std::abs
can convert to a pointer to function with the desired signature. Or we could, except that C++20 doesn't allow forming pointers to most standard library functions.Leaving this original answer here for information. It would be a valid pattern for an overloaded function name which is not in the standard library. And it often works anyway, but we shouldn't count on that to continue with newer library versions.
See a test on godbolt.
When this technique is used on a name that includes matching function templates, the constraint can also be satisfied if no non-template function is an exact match, but one most-specialized function template can deduce its arguments so that the type of a specialization is exactly
T(T)
.(There is a
std::abs
function template declared in<complex>
, but it can never exactly match typeT(T)
.)