Следуя с Монада продолжения кортежа, скажу, я определяю функтор std_tuple
перейти от категории монад-кортежа к std::tuple
:
auto std_tuple = [](auto... args)
{
return [=](auto f){ return f(std::make_tuple(args...)); };
};
Теперь мы можем использовать монад-кортежи в контекстах, ожидающих std::tuple
:
template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}
int main()
{
tuple(1,2,3)(std_tuple)(f);
}
Все идет нормально. За исключением того, что это не компилируется. Clang 3.4.1 жалуется:
примечание: шаблон кандидата игнорируется: невозможно определить аргумент шаблона ‘$ auto-1-0’
на f(t)
позвоните внутрь std_tuple
функтор.
Это правильно, не являются ли эти аргументы шаблона выводимыми? В случае положительного, почему?
Простой случай, который воспроизводит вашу проблему:
void f(int) {}
void f(double) {}
template<class T> void call_with_3( T t ) { t(3); }
int main() {
call_with_3( f );
}
Здесь мы можем увидеть то, что f
Вызов не может быть определен в точке, где мы передаем его call_with_3
, Теперь у вас, похоже, нет нескольких перегрузок (у вас есть только одна f
!), но…
template
это не экземпляр. template
Функция — это фабрика функций, а не функция.
Там нет ни объекта, ни ценности, которую можно было бы передать.
Когда вы передаете имя функции в качестве аргумента, разрешение перегрузки включается. Если целевой тип известен (как ссылка на функцию или указатель), он используется для разрешения перегрузки для имени функции.
В этом случае вы передаете имя функции template
(auto
аргумент), поэтому разрешение перегрузки не может быть выполнено, поэтому не может быть найдено конкретное значение, поэтому вы получите ошибку.
Вы можете создать объект, результатом которого является разрешение перегрузки для вызываемых аргументов с заданным именем функции. Я называю их перегрузкой множества объектов.
static struct f_overload_set_t {
template<class... Args>
auto operator()(Args&&... args) const {
return f(std::forward<Args>(args)...);
}
} f_overload_set;
в C ++ 11 вам нужен ->decltype( f( std::declval<Args>()... ) )
после const
,
Сейчас f_overload_set(blah)
будет (при вызове) будет (почти) делать то, что происходит, когда вы f(blah)
, но f_overload_set
это реальный объект. Таким образом, вы можете передать его.
Макросы, которые генерируют такие наборы перегрузки, относительно легко написать. Они также могут использовать лямбды, поскольку вышеприведенное очень похоже на лямбду без сохранения состояния, если подумать.
Хорошая вещь в лямбда-генераторе-генераторах макросов без сохранения состояния состоит в том, что он может быть создан в точке использования. Из комментария @ dyp выше:
#define OVERLOAD_SET( FUNC )\
([](auto&&... args){\
return FUNC(std::forward<decltype(args)>(args)...);\
})
(примечание: без скобок FUNC
, так как это блокирует ADL). Скобки вокруг всего остального, потому что в противном случае, если используется внутри операции индекса (operator[]
), это будет проанализировано как [[
запуск атрибута, среди других мест (спасибо @ecatmur))
который делает ваш код:
template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}
int main() {
tuple(1,2,3)(std_tuple)(OVERLOAD_SET(f));
}