2014-12-05 57 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