"Overload"函数模板基于函数对象operator()签名

Pra*_*tic 1 c++ templates overloading c++11

考虑一个函数模板的示例,该函数模板将函数引用作为其第一个参数.它根据第一个参数的函数签名重载.每个重载的主体为其签名适当地提供第一个参数函数.

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;
}
Run Code Online (Sandbox Code Playgroud)

由于过载,对这些函数中的任何一个的引用都可以作为第一个arg传递:

int foo(const int x);
int bar(const int x, const std::vector<int>& v);
Run Code Online (Sandbox Code Playgroud)

这样做是透明的:

const auto a = map_vec(foo, v);
const auto b = map_vec(bar, v);
Run Code Online (Sandbox Code Playgroud)

上面使用的重载策略不适用于函数对象,因为对象本身本身没有签名.假设感兴趣的功能对象如下.

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_;
};
Run Code Online (Sandbox Code Playgroud)

如何根据调用方式更改函数模板以接受函数对象和函数以及重载?

具体来说,我希望这个编译:

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

const AddNumMulSize add2mulsz(2);
const auto d = map_vec(add2mulsz, v);
Run Code Online (Sandbox Code Playgroud)

clang给出的错误消息非常清楚,并且符合您的预期.

错误:没有用于调用'map_vec'的匹配函数

候选函数[使用T = int]不可行:第一个参数没有已知的从'const AddNum'到'typename MapTtoT :: type&'(又名'int(&)(const int)'的转换

更新:这个问题的C++ 98版本

基于C++ 98中的函数对象operator()签名的"重载"函数模板

0x4*_*2D2 6

更改您的签名以通常采用类型的函数对象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>{});
Run Code Online (Sandbox Code Playgroud)

Demo