Если вы ослабляете (устраняете) требование не-шаблонности, это делается через SFINAE, например:
template<class T>
typename std::enable_if<
std::is_base_of<A1, T>::value
&& std::is_base_of<A3, T>::value>::type
Foo(T const &);
В противном случае вам понадобится больше хитростей, а точнее, вам все равно понадобится шаблонное решение, хотя шаблоны должны быть изолированы в другом месте. Одним из возможных решений, кратко изложенных, было бы что-то вроде:
struct A1 {};
struct A2 {};
struct B: A1, A2 {};
struct C: A1, A2 {};
template<class B1, class B2> struct product {
template<class T> product(T &t): b1(t), b2(t) {}
operator B1 &() const { return b1; }
operator B2 &() const { return b2; }
private:
B1 &b1;
B2 &b2;
};
void f(product<A1, A2> p) {}
int main() {
B b;
C c;
f(b);
f(c);
}
(Для краткости я ограничил количество оснований двумя - вы можете фактически расширить этот многовариантный адаптер для произвольной упаковки - и оставив в стороне конструкцию из временных объектов - все еще не ракетостроение.)