簡體   English   中英

將任意類的成員函數指針存儲為類實例變量

[英]Storing member function pointer from arbitrary class as class instance variable

關於SO,有幾個問題將通過函數指針作為參數/自變量尋址( 此處此處此處等)。 實際上,前幾天我問了一個相關的問題 但是,這個問題有些不同。

我的問題是我正在編寫一個非常靈活的類。

我現在擁有的功能適用於非成員函數。 它張貼在下面

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

但是,當我需要使用指向某個其他任意類的成員函數的指針進行初始化時,這變得很困難,對我而言,不幸的是,就我而言,這恰好是一個常見的用例。

這個答案表明

在適當的C ++接口中,您可能需要看一下讓函數采用模板化參數的函數對象,以使用任意類類型。

但是,我無法進行此編譯。 我已經嘗試對我的typedef進行模板化(使用C ++ 11別名方法),並且嘗試向該類中添加第二個模板參數來處理這些成員函數的調用類,但是這兩種方法均無效。

這個Q / A似乎正朝着我要去做的方向發展,但是我無法做出正面或反面的結論。

  1. 有人可以解釋一下我如何修改我的類以處理傳入的任意成員函數指針嗎?
  2. 此外,是否可以使其處理任意成員函數或非成員函數?
  3. 最后,是否可以使用模板執行此操作?

作為記錄,我試圖避免使用functional頭,但是不使用它可能是一個愚蠢的事情。

如果希望MyClass成為可以同時包含兩個類型的自由函數指針的模板:

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的自由函數指針專門化,並且:

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成員函數。 帶有T const &參數並返回bool的C成員函數很有可能是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

沒有第二個,嘗試使用const的obj調用MyClass<T,C>::callFirstMyClass<T,C>::callSecond都將無法編譯。

對於演示該解決方案的程序,您可以附加:

#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;
}

現場觀看

您說過要此模板“非常靈活”。 圖示的解決方案適合您的示例,但是您可能會想知道它並不盡如人意 對於自由函數和成員函數,還有其他可變參數模板參數,您的模板可以存儲和調用具有任意返回類型和任意類型實參數量的[member]函數。 請參閱此問題和答案。

我將輕松創建一個幫助器對象,該對象將存儲您要使用的類型:

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]

此處查看現場演示。

您需要做的就是bind成員函數指針的對象實例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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM