2017-02-12 66 views
4

有幾個SO地址傳遞函數指針作爲參數/參數(here,here,here等)。事實上,那天我問了一個related question。但是,這個問題有點不同。將任意類的成員函數指針作爲類實例變量存儲起來

我的問題是我正在寫一個我想要非常靈活的課程。

我現在對非成員函數有效。這是下面貼

template <typename T> 
class MyClass 
{ 
    private: 
     typedef double (*firstFunctionPtr) (const T &var); 

     typedef bool (*secondFunctionPtr)(const T &var); 

     // Function pointers as member variables 
     firstFunctionPtr _firstFunc; 
     secondFunctionPtr _secondFunc; 

    public: 
     inline MyClass(firstFunctionPtr firstFunc, 
      secondFunctionPtr secondFunc); 
}; 

template<typename T> 
MyClass<T>::MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) : 
    _firstFunc(firstFunc), 
    _secondFunc(secondFunc), 
    {} 

然而,這種分崩離析的時候我需要一個指向一些其他的,任意的,一流的,但不幸的對我來說,恰好是一個常見的用例的成員函數初始化我的目的。

This answer表明

在一個適當的C++接口,你可能想看看有你的函數採取模板參數的函數對象使用任意類類型。

但是,我一直無法進行編譯。我試着模擬我的typedefs(使用C++ 11別名方法),並且我已經嘗試向類中添加第二個模板參數來處理這些成員函數的調用類,但都沒有奏效。

This Q/A似乎越來越接近我想要做的事情,但我無法做出正面或反面。

  1. 有人可以請解釋我怎麼可以修改我的類來處理傳遞的任意成員函數指針?
  2. 此外,是否有可能使它可以處理任意成員函數或非成員函數?
  3. 最後,是否可以使用模板做到這一點?

爲了記錄在案,我試圖避免使用functional頭,但它可能是一個傻瓜的差事不使用它。

+0

「函數指針指向任意類」的問題是你需要一個「任意類的對象」來使用它。不,'void *'將不起作用。你最終可能會用'std :: bind'和'std :: function',因爲它不再依賴於無限的任意類。 – MSalters

回答

2

如果你想MyClass是可以容納兩個free函數的類型 指針模板:

double (*)(const T &var); 
bool (*)(const T &var); 

對於一些參數類型T,或者成員函數的類型 指針:

double (C::*)(const T &var); 
bool (C::*)(const T &var); 

對於某些參數類型CT然後,MyClass必須同時TC進行參數 ,你需要兩個專業:

  1. 哪裏C是一些非類類型
  2. C是任何類類型

萬一(1),非類類型C不可能具有成員函數 ,以便人們可以實現自由函數指針專用化。

在情況(2)中,類C可以是具有成員函數的類,因此一個 將實現成員函數指針專用化。

非班級類型C的明顯選擇是void。因此,我們可以讓C 默認爲void

主模板

template<typename T, typename C = void> 
struct MyClass; 

這樣:

MyClass<T> 

將是T free函數指針專業化和:

MyClass<T,C> 

對於除void以外的任何C,將是成員函數指針專用化。

正如你可能知道你可以用std::enable_ifSFINAE使編譯器 選擇一個模板類的一個專業方向或另一個,這取決於它的模板之一 參數是否U滿足一些編譯時的測試。你可以採取 即接近這裏,但另外一個是可用,不需要設備:

與主模板開始,我們只是想有:

自由功能專業化

template<typename T> 
struct MyClass<T> 
{ 
    ... for free function pointers ... 
}; 

和:

會員功能專業化

template<typename T, typename C> 
struct MyClass<T,C> 
{ 
    ... for member function pointers ... 
}; 

但是我們不能這樣做,因爲成員函數「specialization」具有與主模板完全相同的模板參數 。這意味着它不是 專業化,編譯器將不會允許它。

但是,只需簡單地爲主要 模板提供一個不需要的默認模板參數,但其存在的 即可讓這兩個專業化站點都可以輕鬆解決該問題。

新的主模板

template <typename T, typename C = void, typename Default = void> 
struct MyClass; 

因此,這裏是一個說明性的解決方案:

// Primary template 
template <typename T, typename C = void, typename Default = void> 
struct MyClass; 

// Free function specialization 
template <typename T> 
struct MyClass<T> 
{ 
    using firstFunctor_t = double(*)(T const &); 
    using secondFunctor_t = bool(*)(T const &); 

    MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc) 
    : _firstFunc(firstFunc), 
     _secondFunc(secondFunc) 
    {} 

    double callFirst(T const & var) { 
     return _firstFunc(var); 
    } 

    bool callSecond(T const & var) { 
     return _secondFunc(var); 
    } 

private: 

    firstFunctor_t _firstFunc; 
    secondFunctor_t _secondFunc; 
}; 

// Member function specialization 
template <typename T, typename C> 
struct MyClass<T,C> 
{ 
    using firstFunctor_t = double(C::*)(T const &); 
    using secondFunctor_t = bool(C::*)(T const &) const; 

    MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc) 
    : _firstFunc(firstFunc), 
     _secondFunc(secondFunc) 
    {} 

    double callFirst(C & obj, T const & var) { 
     return (obj.*_firstFunc)(var); 
    } 

    double callFirst(C const & obj, T const & var) { 
     auto & o = const_cast<C&>(obj); 
     return (o.*_firstFunc)(var); 
    } 

    bool callSecond(C & obj, T const & var) { 
     return (obj.*_secondFunc)(var); 
    } 

    bool callSecond(C const & obj, T const & var) { 
     auto & o = const_cast<C&>(obj); 
     return (o.*_secondFunc)(var); 
    } 

private: 

    firstFunctor_t _firstFunc; 
    secondFunctor_t _secondFunc; 
}; 

在成員函數專業化,發現幾個百分點,你可能 沒有考慮: -

我決定我要存儲的第二個成員函數應該是 const成員函數。 C 參數T const &的參數並返回bool將會是const成員 函數的成員函數更多,不是嗎?如果是的話,那麼const-ness必須的 我在專業化使用成員函數類型定義部分:

using secondFunctor_t = bool(C::*)(T const &) const; 

或嘗試實例化專業化與任何bool (C::*)(T const &) const 將無法​​編譯。

另外,我提供了兩個重載每個MyClass<T,C>::callFirstMyClass<T,C>::callSecond,一個帶有參數:

C & obj, T const & var 

,另一個參數:

C const & obj, T const & var 

沒有第二,嘗試或者調用MyClass<T,C>::callFirstMyClass<T,C>::callSecondobj這是常量將無法 編譯。

對於程序演示該解決方案可以追加:

#include <iostream> 
#include <string> 

double foo(std::string const & s) 
{ 
    return std::stod(s); 
} 

bool bar(std::string const & s) 
{ 
    return s.size() > 0; 
} 

struct SomeClass 
{ 
    SomeClass(){}; 
    double foo(std::string const & s) { 
     return ::foo(s); 
    } 

    bool bar(std::string const & s) const { 
     return ::bar(s); 
    } 
}; 

int main() 
{ 
    MyClass<std::string> my0{foo,bar}; 
    std::cout << std::boolalpha; 
    std::cout << my0.callFirst("1.11") << std::endl; 
    std::cout << my0.callSecond("Hello World") << std::endl; 

    MyClass<std::string,SomeClass> my1{&SomeClass::foo,&SomeClass::bar}; 
    SomeClass thing; 
    std::cout << my1.callFirst(thing,"2.22") << std::endl; 
    std::cout << my1.callSecond(thing,"Hello World") << std::endl; 

    SomeClass const constThing; 
    std::cout << my1.callFirst(constThing,"3.33") << std::endl; 
    std::cout << my1.callSecond(constThing,"Hello World") << std::endl; 
    return 0; 
} 

See it live

你說,你想這個模板是「非常靈活」。 圖解解決方案適合您的示例,但您可能對 感興趣,知道它不是差不多儘可能靈活。 對於免費函數和成員函數,還有其他variadic template 參數,您的模板可以使用任意類型的任意參數的任意返回類型和任意數量來存儲和調用[成員]函數。 請參閱this question和 的答案。

+0

這就是我所希望的那種答案。謝謝!一個(主觀)問題。有多餘的模板參數是不好的做法嗎?這似乎是要問問題有沒有故意使用的信息 – marcman

+0

@marcman嗨。不,這不是一個壞習慣;它是TMP的庫存交易。看(示例結束)如何[std :: enable_if](http://en.cppreference.com/w/cpp/types/enable_if) 用於啓用類模板專門化之間的SFINAE選擇。 *它的作用是向默認的模板參數追加 模板,該模板在任何專業化中都沒有用處,但允許我們使用控制*編譯器將在任何情況下實例化的特殊功能。同樣在這裏。 –

0

您只需要將bind作爲成員函數指針的對象實例作爲第一個參數。

struct foo { 
    float bar1(const type &var); 
    bool bar2(const type &var); 
}; 
foo my_foo; 
auto f1 = std::bind(&foo::bar1, my_foo, _1); 
auto f2 = std::bind(&foo::bar2, my_foo, _1); 
MyClass<type> my_obj(f1, f2); 
+0

但我需要的類型被模板化,而不是通過參數傳遞,以便與我的成員變量一起工作。如何改變你的答案 – marcman

+0

好吧,只需補充一點。這回答了關於從成員函數中創建函數指針的操作。 –

+0

好吧,等一下。所以'auto f1 = std :: bind(&foo :: bar1,my_foo); auto f2 = std :: bind(&foo :: bar2,my_foo);'應該可以存儲爲我在OP中聲明的成員變量?或者說,這些語句將允許成員函數指針被存儲爲非成員函數指針? – marcman

1

我會sugest建立一個輔助對象將存儲您要使用的類型:

template <typename RETURN, typename TYPE, typename CLASS> 
struct function_pointer 
{ using type_t = RETURN (CLASS::*)(const TYPE &); }; 

template <typename RETURN, typename TYPE> 
struct function_pointer<RETURN, TYPE, std::nullptr_t> 
{ using type_t = RETURN (*)(const TYPE &); }; 

這種類型的,如果一個類被作爲第三個參數和提供的服務將創建一個成員函數指針函數指針,否則。現在,我們可以在MyClass使用這個幫手:

template <typename T, typename CLASS = std::nullptr_t> 
class MyClass 
{ 
    using firstFunctionPtr = typename function_pointer<double, T, CLASS>::type_t; 
    using secondFunctionPtr = typename function_pointer<bool, T, CLASS>::type_t; 

    // Function pointers as member variables 
    firstFunctionPtr _firstFunc; 
    secondFunctionPtr _secondFunc; 

public: 
    inline MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) : 
     _firstFunc(firstFunc), 
     _secondFunc(secondFunc) 
    {} 

    void call_first(CLASS &c, const T&v) { (c.*_firstFunc)(v); } 
    void call_second(CLASS &c, const T&v) { (c.*_secondFunc)(v); } 

    void call_first(const T&v) { (_firstFunc)(v); } 
    void call_second(const T&v) { (_secondFunc)(v); } 
}; 

我添加call_*功能只是爲了顯示一個用例,這將是如下:

// Some class with the expected function signatures 
struct S1 
{ 
    int i = 0; 
    double d(const int &) { std::cout << i << ' ' << __PRETTY_FUNCTION__ << '\n'; return{}; } 
    bool b(const int &) { std::cout << i << ' ' << __PRETTY_FUNCTION__ << '\n';  return{}; } 
}; 

// Another class with the expected function signatures 
struct S2 
{ 
    double d(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; } 
    bool b(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; } 
}; 

// Free function with which could have the expected function signature 
template <typename R> 
R f(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; } 

使用MyClass具有任意類( S1):

S1 a{1}, b{2}; 
S2 c, d; 
MyClass<int, S1> MCiS1(&S1::d, &S1::b); 
MCiS1.call_first(a, 111); // Prints -> 1 double S1::d(const int&) 
MCiS1.call_second(b, 222); // Prints -> 2 bool S1::b(const int&) 
MCiS1.call_first(c, 111); // Error decltype(c) is not S1. 
MCiS1.call_second(d, 222); // Error decltype(d) is not S1. 

MyClass使用與不同的類(S2):

MyClass<int, S2> MCiS2(&S2::d, &S2::b); 
MCiS2.call_first(c, 111); // Prints -> double S2::d(const int&) 
MCiS2.call_second(d, 222); // Prints -> bool S2::b(const int&) 
MCiS2.call_first(a, 111); // Error decltype(c) is not S2. 
MCiS2.call_second(b, 222); // Error decltype(d) is not S2. 

使用MyClass與非成員函數:

MyClass<int> MCi(f<double>, f<bool>); 
MCi.call_first(111); // Prints -> R f(const int&) [with R = double] 
MCi.call_second(222); // Prints -> R f(const int&) [with R = bool] 

檢查現場演示Here