2017-10-16 78 views
1

我試圖創建一個函數重載因此它只能結合(工程)成員函數。我接過來一看在std::mem_fn http://en.cppreference.com/w/cpp/utility/functional/mem_fn模板語法來代替唯一的成員函數

template <class Ret, class T> 
/* unspecified */ mem_fn (Ret T::* pm); 

函數簽名所以我構建我的參數,例如

template <typename R, typename F> 
auto call_me(R C::* func) { 
    return (mContainer.*func); 
} 

不過,後來我得到這個錯誤

.\template.cpp: In function 'int main()': 
.\template.cpp:29:49: error: no matching function for call to 'MyClass<int, std::vector<int> >::call_me(std::vector<int>::size_type (std::vector<int>::*)() const noexcept)' 
    cout << test.call_me(&std::vector<int>::size) << endl; 
               ^
.\template.cpp:16:10: note: candidate: template<class R, class F> auto MyClass<T, C>::call_me(R C::*) [with R = R; F = F; T = int; C = std::vector<int>] 
    auto call_me(R C::* func) { 
      ^~~~~~~ 
.\template.cpp:16:10: note: template argument deduction/substitution failed: 
.\template.cpp:29:49: note: couldn't deduce template parameter 'F' 
    cout << test.call_me(&std::vector<int>::size) << endl; 

我之所以我試圖做到這一點,所以我可以有一個適用於通用lambda和功能對象的重載以及適用於membe的另一個重載r功能指針。這是我想要實現的一個最簡單的例子。我知道這個問題有點令人困惑,所以如果需要的話請隨時要求澄清。

#include <vector> 
#include <iostream> 

using namespace std; 

template <typename T, typename C> 
struct MyClass { 
    // This doesnt work because of SFINAE 

    template <typename F, typename... A> 
    auto call_me(F func, A... args) { // lambda version 
     return func(args...); 
    } 

    template <typename R, typename F> 
    auto call_me(R C::* func) { // member function version 
     return (mContainer.*func); 
    } 

    C mContainer; // this is private in my actual code 

}; 


int main() { 
    MyClass<int, std::vector<int> > test;; 

    // these two calls will call the member function version of the overload 
    cout << test.call_me(&std::vector<int>::size) << endl; 

    using insert_func_t = std::vector<int>::iterator(std::vector<int>::*)(std::vector<int>::const_iterator, const int&); 
    test.call_me(static_cast<insert_func_t>(&std::vector<int>::insert), test.mContainer.begin(), 4); 

    // this call will call the lambda version of the overload 
    cout << test.call_me([](std::vector<int>& in){ in.push_back(5); }); 

    return 0; 
} 
+1

'的std :: invoke'提供統一的調用語法。如果你還沒有在標準庫中使用它,它不需要編寫C++ 17功能,而且現在有獨立的實現。通過使用'invoke'函數,您不需要通過調用差異來污染其餘的代碼。 – chris

+0

@克里斯我只是檢查了這一點,但它仍然給我留下搞清楚,如果提供的參數是一個成員函數或它的一個可調用對象的問題。 – Aryan

+0

@IgorTandetnik嗯,我有這個嘗試也行,但它仍然無法正常工作的r(C :: * FUNC)()'和'用住宅(丙類:: * FUNC)(參數...)'用'Args'是基於您的代碼的另一模板參數 – Aryan

回答

4

可以覆蓋這兩種情況下與std::invoke

template <typename F, typename... A> 
auto call_me(F func, A... args) { // lambda version 
    return std::invoke(func, mContainer, args...); 
} 

對於函數對象,比如你關閉,這叫operator()。對於成員函數,它將參數與要使用的對象作爲前綴,然後使用適當的語法調用它。換句話說,你的工作已經完成了。

你也可以採取完善轉發到:

template <typename F, typename... A> 
auto call_me(F&& func, A&&... args) { // lambda version 
    return std::invoke(std::forward<F>(func), mContainer, std::forward<A>(args)...); 
} 
0

還有更多的功能(或類方法),比它的返回類型。

功能有參數。每個參數都有一個類型。函數參數的類型是函數簽名的一部分。這包括具有零參數的函數。

如果你想有一個模板函數綁定到任意類的成員函數,模板函數必須明確地綁定到類的成員函數的參數類型,也。這是C++的類型安全的一個基本方面:

template<class Ret, 
    class T, 
    class ...Args> 
void mem_fn(Ret (T::*pm)(Args...)) 
{ 
} 

class foo { 

public: 

    void bar(int); 
}; 

void foo() 
{ 
    mem_fn(&foo::bar); 
} 

如果你有興趣只綁定到沒有參數的成員函數,你可以放棄的可變參數模板參數,你必須明確相應的綁定模板:

template<class Ret, 
    class T> 
void mem_fn(Ret (T::*pm)()) 
{ 
} 

class foo { 

public: 

    void bar(); 
}; 


void foo() 
{ 
    mem_fn(&foo::bar); 
} 

請注意,該模板只會綁定到不帶參數的類成員函數。例如,它不會綁定到成員函數void bar(int)

+0

和cv/ref限定符(和C省略號) - >'Ret(C :: *)(參數...,...)const volatile &&'。 – Jarod42

+0

@ Jarod42我是否必須爲每一個重載? (const,const volatile,const noexcept ...等)?還是有辦法簡化它? – Aryan

+0

@Aryan:如果你不依賴已經處理過的東西(作爲'std :: invoke'),你將不得不自己處理它。 – Jarod42