Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default fallback overload of a function template

Short general question:

Is there a way to provide a default "fallback" overload of a function template? I read about some techniques here on Stack Overflow, but one requires using a variadic function (f(...)) which has some serious drawbacks in my case, and other requires listing all the type combinations that are overloaded, which is too verbose and not automated enough for my needs. This question was asked, but it's a few years old now, so I was wondering if there are some new solutions to this using the features of the latest standards and maybe even some solutions that will be possible in C++20 using concepts.

Long detailed question:

I'm trying to implement something like dynamic typing in C++. The point is, that I have several "abstract" types like Bool, Integer, Decimal, String etc. that are represented with some built-in types i.e. Integer is stored as a long long, but any integral type except bool is converted to it.

Now I started to implement operators. I could implement every possible overload (i.e. Bool + Integer, Integer + Integer, ...) manually, but I'd like to have only one implementation per some "category" i.e. Decimal + any_lower_type_than<Decimal> (commutative), where any_lower_type_than<Integer> refers to certain hierarchy of the abstract types. I have all the needed metafunctions to distinguish the categories and hierarchy implemented. I then use SFINAE with those metafunctions to provide the definitions. For example:

// decimal + lower, commutative:
template <typename first_t, typename second_t,
    std::enable_if_t<commutative_with_lower<first_t, second_t, Decimal>::value>* = nullptr>
Decimal operator+ (const first_t& first, const second_t& second) {
    return first + second;
}

// string * lower than integer, commutative:
template <typename first_t, typename second_t,
    std::enable_if_t<commutative_with_lower_than<first_t, second_t, String, Integer>::value>* = nullptr>
String operator* (const first_t& first, const second_t& second) {
    String string_arg = is_string<first_t>::value ? first : second;
    auto other_arg = is_string<first_t>::value ? second : first;
    String result = "";
    for (decltype(other_arg) i = 0; i < other_arg; ++i)
        result += string_arg;
    return result;
}

So that's all OK, but what I need now is some default "fallback" implementation, that gets called as a last resort, when no match was found. So I need to somehow force the fallback implementation to be the worst possible match, but at the same time be a match for any case.

I read about the variadic function "sinkhole" being a worse match than anything. So the natural approach would be to provide the fallback as operator+ (...), which is impossible of course. So I changed all the operators to regular functions e.g. operator_add and then implemented the operator+ itself at a higher level by calling the appropriate operator_add overload, where the default overload is implemented as a variadic function operator_add(...). But then I encountered another problem. Variadic functions accept only trivial types. But I need to use this technique for user-defined types as well.

So the question is: Is there any alternative technique to the variadic function sinkhole that would ensure the worst possible match? Is there maybe even a way to do this without modifying the functional arguments, maybe by modifying the template arguments, so that I could use the technique on actual operators, that have fixed functional arguments signature?

like image 247
egst Avatar asked Oct 18 '25 01:10

egst


1 Answers

So the question is: Is there any alternative technique to the variadic function sinkhole that would ensure the worst possible match?

You might use hierarchy to order overloads:

template <std::size_t N> struct overloadPriority : overloadPriority<N -1> {};
template <> struct overloadPriority<0>{};

then

template <typename T>
std::enable_if_t<MyTrait5<T>::value> foo_impl(T&&, overloadPriority<5>) {/*..*/}

template <typename T>
std::enable_if_t<MyTrait4<T>::value> foo_impl(T&&, overloadPriority<4>) {/*..*/}

// ...

template <typename T>
void foo_impl(T&&, overloadPriority<0>) {/*..*/} // Fallback


template <typename T>
auto foo(T&& t)
{
    return foo_impl(std::forward<T>(t), overloadPriority<42>{});
    // number greater or equal to max overload_priority used by that overload.
}
like image 198
Jarod42 Avatar answered Oct 20 '25 14:10

Jarod42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!