Шаблон функции "Перегрузка" на основе сигнатуры оператора функционального объекта ()

Рассмотрим этот пример шаблона функции, который принимает ссылку на функцию в качестве первого аргумента. Он перегружен на основе сигнатуры функции этого первого аргумента. Тело каждой перегрузки передает функцию 1-го аргумента соответствующим образом для своей подписи.

template<typename T>
struct MapTtoT { typedef T (type)(const T); };

template<typename T>
std::vector<T> map_vec(
        const typename MapTtoT<T>::type& fnc,
        const std::vector<T>& source)
{
    std::vector<T> dest;
    dest.reserve(source.size());
    for (const auto i : source)
    {
        dest.emplace_back(fnc(i));
    }
    return dest;
}

template<typename T>
struct MapTandVectoT { typedef T (type)(const T, const std::vector<T>&); };

template<typename T>
std::vector<T> map_vec(
        const typename MapTandVectoT<T>::type& fnc,
        const std::vector<T>& source)
{
    std::vector<T> dest;
    dest.reserve(source.size());
    for (const auto i : source)
    {
        dest.emplace_back(fnc(i, source));
    }
    return dest;
}

Из-за перегрузки ссылка на любую из этих функций может быть передана как 1-й аргумент:

int foo(const int x);
int bar(const int x, const std::vector<int>& v);

И это прозрачно:

const auto a = map_vec(foo, v);
const auto b = map_vec(bar, v);

Стратегия перегрузки, использованная выше, не будет работать для функциональных объектов, поскольку сам объект не имеет сигнатуры как таковой. Предположим, что интересующие объекты функции следующие.

class AddNum
{
public:
    AddNum(const int num) : num_(num) {}

    int operator()(const int x) const
    {
        return x + num_;
    }

private:
    const int num_;
};

class AddNumMulSize
{
public:
    AddNumMulSize(const int num) : num_(num) {}

    int operator()(const int x, const std::vector<int>& v) const
    {
        return (x + num_) * v.size();
    }

private:
    const int num_;
};

Как я могу изменить шаблоны функций так, чтобы они принимали как объекты функций, так и функции и перегрузки, в зависимости от того, как должен выполняться вызов?

В частности, я хочу, чтобы это скомпилировать:

const AddNum add2(2);
const auto c = map_vec(add2, v);

const AddNumMulSize add2mulsz(2);
const auto d = map_vec(add2mulsz, v);

Сообщение об ошибке, которое выдает clang, совершенно ясно и соответствует ожидаемому.

ошибка: нет подходящей функции для вызова 'map_vec'

функция-кандидат [с T = int] недопустима: нет известного преобразования из 'const AddNum' в 'typename MapTtoT::type &' (aka 'int (&)(const int)') для 1-го аргумента

Обновление: C++98 версия этого вопроса

Шаблон функции "перегрузки" на основе сигнатуры оператора функционального объекта () в C++98

1 ответ

Решение

Измените свою подпись, чтобы в общем случае взять объект функции типа F, то вы можете использовать выражение-SFINAE, чтобы ограничить перегрузки на основе того, что F можно вызвать с помощью:

template<typename F, typename T>
auto map_vec(F&& fnc, const std::vector<T>& source)
    -> decltype(void(fnc(std::declval<T>())), std::vector<T>{});

template<typename F, typename T>
auto map_vec(F&& fnc, const std::vector<T>& source)
    -> decltype(void(fnc(std::declval<T>(), source)), std::vector<T>{});

демонстрация

Другие вопросы по тегам