Ещё одна концептуальная беда — это невозможность (?) вытащить сигнатуру изнутри наружу.
Покажу на дистиллированном примере.
Пусть у нас есть 2 семейства функций, которые мы хотим склеить.
void f(int&);
void f(const int&);
void g(short&);
void g(long&);
Делаем фасад
void h(auto&& x) -> decltype(f(std::forward<x>(x))) { return f(std::forward<x>(x)); } // hf
void h(auto&& x) -> decltype(g(std::forward<x>(x))) { return g(std::forward<x>(x)); } // hg
Эти два шаблона говорят: "если аргумент x можно передать в f (или g, соответственно), — то давайте передадим".
Разумеется, каждый из шаблонов выбирает наилучшую подходящую перегрузку. Но наружу них обоих торчит всего-навсего
template<class T> void h(T&&)
а вовсе не
(int&), (const int&), (short&), (long&)
Попробуем передать
f(1); // (const int&)
g(1); // ошибка - ничего не подошло
h(1); // hf(int) --> f(const int&)
int i;
f(i); // (int&)
g(i); // ошибка
h(i); // hf(int&) --> f(int&)
short s;
f(s); // (const int&)
g(s); // (short&)
h(s); // ошибка - подошли hf(short&) --> f(const int&) и hg(short&) --> g(short&)
Если бы каждое из семейств состояло из одной функции, то мы могли бы попробовать извлечь её сигнатуру (boost/function_traits умеет, можем наколхозить руками)
Но для полиморфных функций — шаблонов и перегрузок — этот номер, очевидно, не пройдёт.
Если бы мы могли измерить вес подстановки, как это делает компилятор (точное попадание, cv, decay, шаблон, приведение типа — ну вот это вот всё), то можно было бы как-то наколхозить приоритеты и руками диспетчеризовывать.
Можем ли?
Я такую технику, увы, не знаю. Если знаете, подскажите.