Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload based on existence of dependent type

I have two templated functions with the same name (foo). Their signatures differ only in the type of the second parameter, which is dependent on the template parameter T. What has surprised me is that I can use this to overload depending on whether T::A or T::B is an existent type. Is this something which is specially provided for in the standard (if so, a reference would be much appreciated), or am I just being dense in not recognising this as basic overload resolution?

#include <iostream>
using namespace std;

template<typename T>
void foo(T t, typename T::A* a = 0) {
  cout << "Had an A" << endl;
}

template<typename T>
void foo(T t, typename T::B* b = 0) {
  cout << "Had a B" << endl;
}

struct HasAnAType {
  typedef int A;
};

struct HasABType {
  typedef int B;
};

struct HasAAndBTypes {
  typedef int A;
  typedef int B;
};

int main() {
  HasAnAType a;
  HasABType b;
  HasAAndBTypes ab;

  foo(a);  // prints "Had an A"
  foo(b);  // prints "Had a B"
  foo(ab); // won't compile: 'ambiguous call to overloaded function'
}

For background, I discovered this was possible when looking into the implementation of std::enable_shared_from_this, which relies on this type of overloading.

like image 641
atkins Avatar asked Feb 09 '23 22:02

atkins


2 Answers

Thanks to SFINAE, the overload void foo(T t, typename T::B* b = 0) is removed when T::B doesn't exist (similar for T::A)

So when both are available, both overloads are viable, and so the call is ambiguous.

like image 161
Jarod42 Avatar answered Feb 16 '23 03:02

Jarod42


This is how the spec defines overload resolution:

Overload resolution is a mechanism for selecting the best function to call given a list of expressions that are to be the arguments of the call and a set of candidate functions that can be called based on the context of the call.

the selection of the best function is the same in all cases: First, a subset of the candidate functions (those that have the proper number of arguments and meet certain other conditions) is selected to form a set of viable functions. Then the best viable function is selected based on the implicit conversion sequences needed to match each argument to the corresponding parameter of each viable function.

For template functions, there is additional rule:

For each function template, if the argument deduction and checking succeeds, the template arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template.

These rules lead to what is called as SFINAE, in case of overload resolution for template functions .

like image 30
basav Avatar answered Feb 16 '23 04:02

basav