2017-02-12 4 views
4

関数ポインタをパラメータ/引数として渡すというアドレスにはいくつか質問があります(hereherehereなど)。実際、先日、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エイリアシングのアプローチを使用して)のテンプレートを試してみましたが、これらのメンバ関数の呼び出しクラスを処理するためにクラスに2番目のテンプレートパラメータを追加しようとしましたが、どちらのアプローチもうまくいきませんでした。

This Q/Aは私がやろうとしているが、私はそれの頭や尾をすることはできませんどのような方向になっているようです。

  1. 誰かが、任意のメンバー関数ポインタが渡されるのを処理するためにクラスを変更する方法を説明できますか?
  2. さらに、それは、任意のメンバ関数や非メンバ関数のいずれかを扱うことができるようにそれを作ることは可能ですか?
  3. 最後に、テンプレートでこれを行うことは可能でしょうか?レコードの

、私はfunctionalヘッダーを使用しないようにしようとしているが、それを使用しない無駄足かもしれません。

+0

「任意のクラスへの関数ポインタ」の問題は、それを使用するために「任意のクラスのオブジェクト」が必要なことです。そして、いいえ、 'void *'は動作しません。 'std :: bind'と' std :: function'はおそらく無限集合の任意のクラスに依存しないので、おそらく 'std :: bind'と' std :: function'で終わるでしょう。 – MSalters

答えて

2

あなたは、両方のフリー機能タイプの ポインタ保持できるテンプレートであることをMyClassをしたい場合:

:いくつかのパラメータの型 T、あるいはメンバー関数の型の ポインタのため

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

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

いくつかのパラメータタイプの場合CおよびTそして、MyClassTC両方で パラメータ化する必要があり、次の2つの専門分野が必要です:CCは、ケース内の任意のクラス型

あるいくつかの非クラス型

  • ある

    1. を(1)の場合、非クラス型Cはメンバ関数 を持つことができないため、自由関数ポインタの特殊化を実装します。

      ケース(2)の場合、クラスCはメンバ関数を持つクラスであるため、 はメンバ関数ポインタの特殊化を実装します。

      非クラス型のための明らかな選択Cvoidです。だから

      プライマリテンプレート

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

      MyClass<T> 
      

      Tのための無料の関数ポインタ専門になり、そして:

      だから我々はvoidからC をデフォルトにすることができます
      MyClass<T,C> 
      

      void以外の任意のCの場合は、メンバー関数ポインタの特殊化になります。

      あなたは、コンパイラ は、そのテンプレートの1 がU満たすいくつかのコンパイル時のテストパラメータかどうかに応じて、1つのクラステンプレートの専門または別のものを選んだ作るためにstd::enable_ifSFINAEを使用することができます知っているかもしれませんが。あなたがここに近づい を取ることができるが、別の1はその装置を必要としないが利用可能です:

      主なテンプレートを皮切りに、私たちは持っているしたいと思います:

      フリー機能の特殊

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

      と:

      メンバ関数の特殊

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

      しかし、メンバ関数 "specialization"は、正確に プライマリテンプレートと同じテンプレートパラメータを持っているため、これを行うことはできません。つまり、 ではなく、コンパイラはそれを許可しません。

      しかし、プライマリ テンプレートに不要なデフォルトのテンプレートパラメータを1つ追加するだけで、それらの特殊化が両方を許可する プレゼンスが存在するようにすることで、簡単にその問題を取り除くことができます。だからここ

      新しいプライマリテンプレート

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

      、あなたが と考えられていない可能性がありますポイントのカップルに気づく:を -

      私が保存したい第2のメンバー機能は constメンバ関数です。メンバー関数CT const &という引数をとり、boolを返す可能性は、constメンバー の関数になるでしょうか?そしてもしそうなら、const-nessは私が専門で使用 メンバー関数の型定義の一部でなければならないこと:

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

      または任意のbool (C::*)(T const &) const で専門のインスタンス化の試行をコンパイルに失敗します。引数と

      C & obj, T const & var 
      

      と他:

      また、IはMyClass<T,C>::callFirstMyClass<T,C>::callSecondのそれぞれのための2つのオーバーロード、引数を有するものに設けられた第二なけれ

      C const & obj, T const & var 
      

      を、いずれか呼び出そうMyClass<T,C>::callFirst またはMyClass<T,C>::callSecondobj(constの場合)は、 コンパイルに失敗します。あなたが追加することができ、このソリューションをデモするためのプログラムのために

      #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

    これは私が望んでいた答えの一種です。ありがとう! 1つ(主観的な)質問しかし。無関係なテンプレートパラメータを持つことは悪い習慣ですか?意図的に未使用の情報を持っていることが問題を求めているようです。 – marcman

    +0

    @marcmanこんにちは。いいえ、それは悪い習慣ではありません。それはTMPの取引中在庫です。 Look(例の終わり)[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); 自動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 &); }; 
    

    クラスは三番目のパラメータであり、aとして提供されている場合、メンバー関数ポインタを作成します。このタイプfunction-pointerでなければなりません。今、私たちはMyClassにこのヘルパーを使用することができます(任意のクラスでMyClassを使用して

    // 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{}; } 
    

    :私はcall_*機能を追加しました

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

    だけで、以下のようになりますユースケースを示すために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. 
    

    異なるクラス(S2)とMyClassの使用:

    非メンバ関数と MyClassを使用して
    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<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を確認してください。

    関連する問題