I've runned into some trouble trying to make friend declarations with sfinae checks (you can just jump into the code sample if you don't want explanations on "why" and "how").
Basically, I have some template class declaring two private member functions. Depending on the instantiation of the template type, I want to use either one or the other function.
So, if I don't want compilation to fail, the private function that I cannot use cannot be instantiated. So, I must called it through an sfinae check (an independent function). Considering it is private, I have to make my sfinae check a friend of my class.
However, I'm unable to do that, as the following (minimal) code illustrates. The things I don't want to change : the prototype of class A (f1 and f2 must remain private), the prototypes of class B1 and B2.
I understand why the stuff in comments fails (or I think I do), but I don't know how to fix it.
#include <iostream>
template<class T> class A;
template<class T>
auto sfinae_check(T& t, A<T>& a, int) -> decltype(t.b1(), void());
template<class T>
auto sfinae_check(T& t, A<T>& a, long) -> decltype(t.b2(), void());
template<class T>
class A
{
void f1() { t.b1(); }
void f2() { t.b2(); }
T& t;
//friend auto sfinae_check<>(T &t, A<T> &a, int);//obviously mismatches everything
//friend auto sfinae_check<>(T &t, A<T> &a, int) -> decltype(t.b1(), void()); //failure : no member named b1
//friend auto sfinae_check<>(T &t, A<T> &a, long) -> decltype(t.b2(), void()); //failure : no member named b2
public:
A(T& t) : t(t) {}
void f() { sfinae_check(t, *this, 0); }
};
template<class T>
auto sfinae_check(T& t, A<T>& a, int) -> decltype(t.b1(), void())
{
a.f1();
}
template<class T>
auto sfinae_check(T& t, A<T>& a, long) -> decltype(t.b2(), void())
{
a.f2();
}
struct B1
{
void b1() { std::cout << "b1" << std::endl; }
};
struct B2
{
void b2() { std::cout << "b2" << std::endl; }
};
int main()
{
B1 b1; B2 b2;
A<B1> a1(b1);
a1.f(); //should print b1
A<B2> a2(b2);
a2.f(); //should print b2
}
This entire scheme can be simplified a great deal (in coupling too) if you forgo the different names f1
and f2
and do tag dispatch instead:
template<int> struct tag{};
template<int i> struct priority : priority<i - 1> {};
template<> struct priority <0>{};
template<class T>
auto sfinae_check(T& t, priority<1>) -> decltype(t.b1(), tag<1>{}) { return {}; }
template<class T>
auto sfinae_check(T& t, priority<0>) -> decltype(t.b2(), tag<0>{}) { return {}; }
template<class T>
class A
{
void f(tag<1>) { t.b1(); }
void f(tag<0>) { t.b2(); }
T& t;
public:
A(T& t) : t(t) {}
void f() { f(sfinae_check(t, priority<1>{})); }
};
No friendship, no almost circular dependencies, and you see the exact output you want. And as icing on the top, adding support for another overload should be fairly easy if the need arises.
The priority of the overloads is also encoded here (thank Jarod42 for reminding me). Since that tags are in an inheritance chain, the second argument priority<1>{}
can be provided to either overloads, but in case both are viable, it will favor the closer match.