簡體   English   中英

我可以有模板函數指針的std :: vector嗎?

[英]Can I have a std::vector of template function pointers?

我有一個模板函數,我想存儲一個指向std::vector內部的指針。

該函數如下所示:

template<typename T> void funcName(T& aT, std::vector<std::string>& fileName){...}

現在我想在std::vector存儲多個指向此類函數的指針。 對於非模板函數,我會這樣做:

typedef std::vector<std::string> string_vt;
typedef void func_t(T&, string_vt&);
typedef func_t* funcPointer;
typedef std::vector<funcPointer> funcPointer_vt;

但是模板函數的正確語法是什么? 我該如何存放?

編輯:首先,感謝您的快速回復。 這是我關於Stack Overflow的第一個問題,所以很抱歉沒有提供足夠的信息。

T的集合是有限的,它可以是ClassA類型或類型classB。 在這些函數模板中,我想用一些硬編碼數據對T(所以ClassA或ClassB)進行更改。 我有8個這樣的函數,它們基本上啟動了一個默認的構造T,其中包含特定於該函數的數據。 在我的程序中,我想啟動2 * 8默認構造的T(8 ClassA和8 ClassB)。 因此,我運行一個for循環,一個接一個地調用一個函數,用函數的body數據啟動我的T對象。

for(int i = 0; i < initT.size(); ++i){
   init_T[i]<T>(someT, fileName);
}

for循環具有與向量內部的函數指針一樣多的迭代。 在每次迭代時,使用一些先前默認構造的T和一些其他參數調用該函數。 最后,目標是使用特定於該功能的數據啟動8個T。

EDIT2:如果有幫助,這里有一些實際的源代碼。 在以下函數模板中,我想訪問我的函數指針向量,以便調用相應的函數。

template<typename T_Relation, typename T_Relation_Vec, bool row>
void bulk_load(initRelation_vt& aInitFunctions, T_Relation_Vec& aRel_Vec, const bool aMeasure, const uint aRuns, const char* aPath)
{
    for(size_t i = 0; i < aRuns; ++i)
    {
        MemoryManager::freeAll();
        aRel_Vec.clear();
        string_vt fileNames;
        for(size_t j = 0; j < aInitFunctions.size(); ++j)
        {
            aRel_Vec.emplace_back(T_Relation());
            aInitFunctions[j]<T_Relation>(aRel_Vec[j], fileNames);
            BulkLoader bl(fileNames[j].c_str(), tuples, aRel_Vec[j], delimiter, seperator);
            Measure lMeasure;
            if(aMeasure)
            {
                lMeasure.start();
            }
            try
            {   
                bl.bulk_load();
                if(row)
                {
                    BulkInsertSP bi;
                    bi.bulk_insert(bl, aRel_Vec[j]);
                }
                else
                {
                    BulkInsertPAX bi;
                    bi.bulk_insert(bl, aRel_Vec[j]);
                }
            }
            catch(std::exception& ex)
            {
                std::cerr << "ERROR: " << ex.what() << std::endl;
            }
            lMeasure.stop();
            if(aMeasure)
            {
                std::ofstream file;
                file.open (aPath, std::ios::out | std::ios::app);
                //print_result(file, flag, lMeasure.mTotalTime());
                file.close();
            }
        }
    }
}

該行是訪問函數模板指針向量的位置。

aInitFunctions[j]<T_Relation>(aRel_Vec[j], fileNames);

模板是靜態多態的高級技術。 在類似C ++的類型語言中,如果沒有靜態多態性,則必須單獨定義所使用的每個實體並精確指示所引用的每個實體。

C ++中靜態多態的機制允許自動指示函數或方法,並將其推遲到通過重載構建。 它允許您通過模板定義多個共享某些特征的實體,並將特定特化的定義推遲到構建,從使用推斷。

請注意,在各種情況下,靜態多態允許單獨的代碼,因此使用和定義的更改是獨立的,這非常有用。

這種機制的重要含義是模板的每個特化都可能是不同類型的。 目前還不清楚,當我在回復時,您是否要在一種類型的容器中存儲指向單個或多個類型的特化的指針。 可能性還取決於函數模板的參數和結果類型。

C ++中的函數具有一種類型,該類型是其參數類型列表及其返回類型的組合。 換句話說,獲取和返回相同類型的兩個函數具有相同的類型。 如果你的函數模板既沒有采用或返回模板參數類型(即T )也沒有模板化類型(例如std::vector<T> ),那么這個函數模板的每個特化都將采用並返回相同的類型,因此是一個相同類型的功能。

    template <typename T>
    int func() { ... }

這個(可以說是無用的)函數模板不帶參數並返回int ,無論T用於專門化模板。 因此,只要將參數定義為int (*f)() ,就可以使用指向它的指針。 在這種情況下,您可以在一個向量中保留指向任何特化的指針。

    typedef std::vector<std::string> string_vt;
    typedef int func_t();
    typedef func_t* funcPointer;
    typedef std::vector<funcPointer> funcPointer_vt;

    funcPointer x = &func<int>;
    funcPointer y = &func<float>;

可以看出,函數模板的每個特化都是相同的類型,並且兩個指針都適合同一個容器。

下一種情況 - 如果函數頭取決於模板參數怎么辦? 每個專業化都會有不同的簽名,即不同的函數類型。 他們所有人的指針將是不同類型的-因此這是不可能的,即使typedef一旦這個指針。

    template <typename T>
    void func(std::vector<T> param) { ... }

在這種情況下,函數模板特化取決於用於專門化的T ,具有不同的類型。

    typedef int func_t_int(std::vector<int>);
    typedef func_t_int* funcPointerInt;
    typedef std::vector<funcPointerInt> funcPointerInt_vt;

    typedef float func_t_float(std::vector<float>);
    typedef func_t_float* funcPointerFloat;
    typedef std::vector<funcPointerFloat> funcPointerFloat_vt;

    funcPointerInt x = &func<int>;

    funcPointerFloat x = &func<float>;

專業化有不同的類型,因為它們采用不同類型的向量。 指針不適合放在同一個容器中。

值得一提的是,在這種情況下,沒有必要單獨定義每個指針類型。 它們可以是模板類型:

    template <typename T>
    using funcPointer = void (*)(std::vector<T>);

現在允許funcPointer<int>用作類型限定符,代替早期的funcPointerInt

    funcPointer<float> y = &func<float>;

在更復雜的情況下,可以創建一個模板,其每個特化都是不同的類型,然后使用具體向量的單個實例來存儲指向模板的一個特化類型的函數的各種指針。 雖然示例中的簡單模板每個類型只能生成一個函數,但由於每個特化都會產生一種類型的函數和一種類型的函數,因此不可能設想獲得各種函數指針的場景,這兩種函數都是專門化的。和通常的功能,可能來自各種來源。 所以這項技術可能很有用。

但另一種情況是,盡管模板的每個特化都是不同類型的,但是需要在單個std::vector存儲指向各種特化的指針。 在這種情況下, 動態多態將有所幫助。 為了存儲不同類型的值,fe。 在一種類型的變量中,指向不同類型函數的指針需要繼承 可以將任何子類存儲在定義為超類的字段中。 請注意 ,這不太可能完成任何事情,也可能不是你真正想要的。

我現在看到兩種普遍的可能性。 使用帶有方法的類模板,該方法繼承自非模板類。

    template <typename T>
    class MyClass : BaseClass
    {
    public:
        T operator()(const T& param, int value);
    }

    MyClass<int> a;
    MyClass<float> b;
    BaseClass* ptr = &a;
    ptr = &b;

雖然本類的每個專業化可以是不同的類型,它們都共享超BaseClass ,所以一個指針指向一個BaseClass實際上可以指向任何一個,和一個std::vector<funcPointerBase>可用於存儲它們。 通過重載operator()我們創建了一個模仿函數的對象。 這樣一個類的有趣屬性是它可以使用參數構造函數創建多個實例。 因此,有效的類模板產生多種類型的特化,反過來,每個專用類都可以生成不同參數化的實例。

    template <typename T>
    class MyClass : BaseClass
    {
        int functor_param;
    public:
        MyClass(int functor_param);
        T operator()(const T& param, int value);
    }  

此版本允許創建不同的實例:

    MyClass<int> a(1);
    MyClass<int> b(2);
    MyClass<float> c(4);
    MyClass<int>* ptr = &a;
    ptr = &b;
    ptr = &c;

我不是算子的專家,只是想提出一般的想法。 如果它看起來很有趣,我建議現在進行研究。

但從技術上講,我們不存儲函數指針,只是常規對象指針。 好吧,如前所述,我們需要繼承來使用一種類型的變量來存儲各種類型的值。 因此,如果我們不使用繼承來交換我們的程序函數來實現動態多態,那么我們必須對指針做同樣的事情。

    template <typename T>
    T func(std::pair < T, char>) {}

    template <typename T>
    using funcPointer = T(*)(std::pair<T, char>);

    template <typename T>
    class MyPointer : BasePointer
    {
        funcPointer<T> ptr;

    public:
        MyPointer(funcPointer<T> ptr);
        T()(std::pair <T, char>) operator*(std::pair <T, char> pair)
        {
            *ptr(pair);
        }
    };

這再次允許創建單個std::vector<BasePointer>來存儲所有可能的偽函數指針。

現在非常重要的一點。 在兩種情況下,你會如何調用它們? 因為在兩種情況下它們都存儲在單個std::vector<> ,所以它們被視為基類型。 特定函數調用需要特定類型的參數並返回特定類型。 如果所有子類都可以以相同的方式執行任何操作,則可以通過在基類中定義這樣的方法(在任一場景中使用仿函數或指針......)來公開它,但是特定的專用函數調用不是那種事情 在所有這些困難之后,您最終想要執行的每個函數調用都是不同的類型,需要不同類型的參數和/或返回不同類型的值。 因此,他們可能永遠不會在通常的情況下適合相同的位置,而不是模板化的代碼,執行中的相同情況。 如果他們這樣做,那么首先不需要動態多態來解決這個問題。

可以做的一件事 - 非常不鼓勵並且可能違背動態多態的目的 - 是在運行時檢測子類類型並相應地繼續。 研究一下,如果你確信你有一個很好的案例來使用它。 但最有可能的是,它可能是一個很大的反模式。

但從技術上講,你可能想做的任何事情都有可能。

如果我正確地理解了你,我可能會有一個非常簡單有效的解決方案:

 template<class...Ts>
 struct functor{
   //something like a dynamic vtable
   std::tuple<void(*)(Ts&,std::vector<std::string>&)...> instantiated_func_ptr;
   template<class T>
   void operator ()(T& aT,std::vector<std::string>& fileName){
     get<void(*)(T&,std::vector<std::string>&)>(instantiated_func_ptr)
                                                          (aT,fileName);
   }
 };

瞧!

在c ++ 17之前, get<typename>未定義,所以我們必須定義它(在上面模板functor的定義之前):

 template<class T,class...Ts>
 struct find_type{
    //always fail if instantiated
    static_assert(sizeof...(Ts)==0,"type not found");
 };
 template<class T,class U,class...Ts>
 struct find_type<T,U,Ts...>:std::integral_constant<size_t,
                              find_type<T,Ts...>::value+1>{};
 template<class T,class...Ts>
 struct find_type<T,T,Ts...>:std::integral_constant<size_t,0>{};
 template<class T,class...Ts>
 constexpr decltype(auto) get(const std::tuple<Ts...>& t){
   return get<find_type<T,Ts...>::value>(t);
 }

並舉例說明如何使用它:

 struct A{
    void show() const{
       std::cout << "A" << "\n";
       }
 };
 struct B{
    void show() const{
       std::cout << "B" << "\n";
       }
 };
 template<class T>
 void func1(T& aT,std::vector<std::string>& fileName){
   std::cout << "func1: ";
   aT.show();
 }
 template<class T>
 void func2(T& aT,std::vector<std::string>& fileName){
   std::cout << "func2: ";
   aT.show();
 }
 template<class T>
 void func3(T& aT,std::vector<std::string>& fileName){
   std::cout << "func3: ";
   aT.show();
 }
 using functorAB = functor<A,B>;
 int main(){
   auto functor1=functorAB{{func1,func1}};//equivalent to functorAB{{func1<A>,func1<B>}}
   auto functor2=functorAB{{func2,func2}};
   auto functor3=functorAB{{func3,func3}};
   auto v=std::vector<functorAB>{functor1,functor2,functor3};
   auto a=A{};
   auto b=B{};
   auto fileNames = std::vector<std::string>{"file1","file2"};
   for(auto& tf:v)
        tf(a,fileNames);  
   for(auto& tf:v)
        tf(b,fileNames);  
 }

在實踐中它只是虛擬調用機制的再現, tuple functortuple是一種虛擬表。 這個代碼並不比你為每個A類和B類編寫一個帶有虛擬運算符()的抽象functor ,然后為每個函數實現它的效率更高......但它更簡潔,更易於維護和可能產生較少的二進制代碼

暫無
暫無

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

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