2014-12-05 24 views
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; 
} 

由于过载的,以任一这些功能的参考可以作为第一ARG通过:

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); 

那铿锵给出的错误信息是非常明确和匹配你所期望的。

error: no matching function for call to 'map_vec'

candidate function [with T = int] not viable: no known conversion from 'const AddNum' to 'typename MapTtoT::type &' (aka 'int (&)(const int)') for 1st argument

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

“Overload” function template based on function object operator() signature in C++98

+0

我现在没有时间来写一个完整的答案,但你可以使用SFINAE:使用'的std :: enable_if'并创建两个特点:'is_callable_with_T'和'is_callable_with_T_and_vector'。 – Angew 2014-12-05 18:07:48

回答

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>{}); 

Demo

+1

有趣的是,我不知道'decltype'会导致这不会被重新定义。 – Praxeolitic 2014-12-05 18:45:36

+0

@Praxeolitic'decltype()'只是其中一部分原因。表达式内部的格式必须正确,否则模板将在重载解析期间被忽略。 – 0x499602D2 2014-12-05 21:37:36