8

As is evident in a question I asked previously, Overload resolution, templates and inheritance, a template overload will be chosen before an overload which requires derived-to-base conversion.

However, is there a way to provide a fallback overload which is only selected as an absolute last resort if there is nothing else that matches? In this particular case enable_if could be used but that would not be extensible, unfortunately.

Like this:

// My library has this and has no knowledge of the possible overloads of foo
template<typename T>
void foo(const T &) { /* Do something */ }

// The user of the library provides this:
void foo(const UserBaseType &) { /* Do something */ }

// User calls foo with object derived from UserBaseType:
foo(UserDerivedType());

In this case, I want the UserBaseType overload to be called, not the template overload.

Community
  • 1
  • 1
Emil Eriksson
  • 2,110
  • 1
  • 21
  • 31

2 Answers2

7

If you're willing to require your users to provide their customization points via Argument Dependent Lookup (ADL), you can accomplish this with the proverbial additional layer of indirection. First, it is possible to determine if ADL for a given name succeeds by providing the worst possible fallback and determining if name lookup selects it[*]:

namespace detail {
  // Simple trait that computes the inverse of std::is_same
  template <typename, typename>
  struct is_different : std::true_type {};
  template <typename T>
  struct is_different<T, T> : std::false_type {};

  // The ellipsis conversion is worse than any other
  // conversion, so overload resolution will choose
  // this declaration of foo only if there is no
  // result from ADL.
  struct tag;
  tag foo(...);

  // Trait that determines if ADL for foo(T) succeeds.
  template <typename T>
  using has_adl_foo =
    is_different<tag,decltype(foo(std::declval<T>()))>;
}

Since the ellipsis conversion is strictly worse than either a standard or user-defined conversion sequence per [over.ics.rank]/2, any reasonable customization of foo provided by the library user will be a better match.

You then need some machinery to dispatch between your fallback implementation and a user-provided customization on the basis of the has_adl_foo trait:

namespace detail {
  // Fallback, used only if ADL fails.
  template <typename T>
  typename std::enable_if<!has_adl_foo<T>::value>::type
  impl(T&&) {
    std::cout << "Fallback\n";
  }

  // Dispatch to foo found by ADL.
  template <typename T>
  typename std::enable_if<has_adl_foo<T>::value,
    decltype(foo(std::declval<T>()))
  >::type
  impl(T&& t) {
    return foo(std::forward<T>(t));
  }
}

template <typename T>
auto foo(T&& t) ->
  decltype(detail::impl(std::forward<T>(t))) {
    return detail::impl(std::forward<T>(t));
}

Users can then provide their customizations fairly simply - simple compared to specializing templates in your library namespace, anyway - by declaring foo overloads in the namespace of their class declarations where ADL can find them (DEMO):

struct UserType {};
struct DerivedUserType : UserType {};

void foo(const UserType&) {
  std::cout << "User extension\n";
}

[*]: ADL Detection technique adapted from @T.C.'s answer to What is a proper way to implement is_swappable to test for the Swappable concept?.

Community
  • 1
  • 1
Casey
  • 41,449
  • 7
  • 95
  • 125
3

The only parameter guaranteed to have lower precedence than anything else are C-style variadics: ..., and that is certainly not what you'd want (or even be able to) use.

I am afraid there is nothing to provide where the only user-side customisation would be providing an overload. If you can tolerate a little bit higher burden on the user, though, you could make it work with a trait class:

template <class T>
struct HasCustomFoo : std::false_type
{};

template <class T, class Sfinae = typename std::enable_if<!HasCustomFoo<T>::value>::type>
void foo(const T &) { /* Do something generic */}

Then, the user of the library must specialise HasCustomFoo for all applicable classes:

template <>
struct HasCustomFoo<UserBaseType> : std::true_type
{};

template <>
struct HasCustomFoo<UserDerivedType> : std::true_type
{};

void foo(const UserBaseType &) { /* Do something user-specific */ }

foo(UserDerivedType()); // This now calls the user-specific function

It's not fully automatic, but at least the solution is in the user's hands and the library can remain generic.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • How is this less work than forcing the user to provide separate overloads for each derived class? I provide `template void foo(const T &) { ... }` and user provides `void foo(const Base &) { ... }`, `void foo(const Derived &) { ... }`. Seems like less code than your example. – Emil Eriksson Feb 09 '15 at 12:46
  • @EmilEriksson They don't have to provide the body. I'm not saying it's perfect, but I can't think of anything better. – Angew is no longer proud of SO Feb 09 '15 at 12:49
  • It's a bit unsatisfactory but that's not your fault, it's the best I can come up with as well. Accepting :) – Emil Eriksson Feb 09 '15 at 13:14
  • @EmilEriksson You might want to hold off the accept, at least for a few days. Maybe some of the real template gurus has something better. – Angew is no longer proud of SO Feb 09 '15 at 13:24