C++20 concept which requires the existence of an exact function signature

1k Views Asked by At

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.

2

There are 2 best solutions below

9
On BEST ANSWER

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.

#include <cmath>

template<class T>
auto abs_diff(const T _a, const T _b) {
    return (_a > _b) ? (_a - _b) : (_b - _a);
}

template<class T> requires requires (T (*fp)(T)) { fp = &std::abs; }
auto abs_diff(const T _a, const T _b) {
    return std::abs(_a - _b);
}

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 type T(T).)

1
On

As a result of the discussion of @aschepler's answer I came up with the following solution which should also work for standard library functions, as std::abs is one:

template<class T>
struct BlockImplicitConversion {
    BlockImplicitConversion(const T& _arg);
    operator T();
};

template<class T>
auto abs_diff(const T _a, const T _b) {
    return (_a > _b) ? (_a - _b) : (_b - _a);
}

template<class T> requires requires (T x) { std::abs(BlockImplicitConversion(x)); }
auto abs_diff(const T _a, const T _b) {
    return std::abs(_a - _b);
}

The rational is, that at most one implicit user-defined conversion is performed. Therefore the use of the proxy class BlockImplicitConversion prevents further implicit casts. At the same time standard conversion sequences are still allowed, e.g. for short the specialized template is called and a cast to int is performed.

Godbolt example