4

テンプレート化されたメンバー関数も持つテンプレート化された C++ クラスがあります。このメンバー関数のテンプレート パラメーターは、特定の方法でクラスのテンプレート パラメーターに依存しています (以下のコードを参照してください)。テンプレート パラメーターの 2 つの異なる値に対して、このクラスをインスタンス化しています (特殊化していません)。この時点まですべてがコンパイルされます。ただし、テンプレート化されたメンバー関数を呼び出すと、最初にインスタンス化されたオブジェクトの呼び出しのみがコンパイルされ、2 番目のオブジェクトはコンパイルされません。テンプレート クラスの 2 番目のインスタンス化に対して、コンパイラがテンプレート化されたメンバー関数をインスタンス化していないように見えます。「g++ filename.cpp」を使用して以下のコードをコンパイルすると、次のエラーが発生します。

filename.cpp:63: エラー: 'Manager<(Base)1u>::init(Combination<(Base)1u, (Dependent2)0u>*)' の呼び出しに一致する関数がありません</p>

これが電話の回線ですb.init(&combination_2)

g++ --version => g++ (Ubuntu/Linaro 4.4.7-1ubuntu2) 4.4.7

uname -a => Linux 3.2.0-25-generic-pae #40-Ubuntu SMP i686 i686 i386 GNU/Linux

enum Base {
  AA,
  BB,
  CC
};

enum Dependent1 {
  PP,
  QQ,
  RR
};

enum Dependent2 {
  XX,
  YY,
  ZZ
};

template<Base B>
struct DependentProperty {
};

template<>
struct DependentProperty<AA> {
  typedef Dependent1 Dependent;
};

template<>
struct DependentProperty<BB> {
  typedef Dependent2 Dependent;
};

template <Base B, typename DependentProperty<B>::Dependent D>
class Combination {
 public:
  void reset() {}
  int o;
};

template <Base B>
class Manager {
 public:
  template <typename DependentProperty<B>::Dependent D,
            template<Base,
                    typename DependentProperty<B>::Dependent> class T>
  void init(T<B, D>* t);
};

template <Base B>
template <typename DependentProperty<B>::Dependent D,
          template<Base,
                  typename DependentProperty<B>::Dependent> class T>
void Manager<B>::init(T<B, D>* t) {
  t->reset();
}

int main(int argc, char** argv) {
  Manager<AA> a;
  Manager<BB> b;
  Combination<AA, PP> combination_1;
  Combination<BB, XX> combination_2;
  a.init(&combination_1);
  b.init(&combination_2);
  return 0;
}

実際のプロジェクトのサンプル コードから Base、Dependent、または Combination に対応するクラスを変更することは現実的ではありません。私が本当に疑問に思っているのは、Manager::init() を定義するための私の構文が間違っているのか、それともこのコードを許可しない C++ または g++ の既知のプロパティ/機能/制約があるかどうかということです。

4

4 に答える 4

2

以下のコードは私のためにコンパイルされます。コードを少し単純化しましたが、それでも同じことを行います。

template <Base B>
class Manager {
 public:
typedef typename DependentProperty<B>::Dependent D;  // if ever you need it
    template <typename TCombinaison>
    void init(TCombinaison* t)
    {
        t->reset();
    }

};

int main(int argc, char** argv) 
{
    typedef Combination<AA, PP> CombinaisonA;
    typedef Combination<BB, XX> CombinaisonB;

    typedef DependentProperty<AA> DependencyPropertyA;
    typedef DependentProperty<BB> DependencyPropertyB;

  CombinaisonA combination_1;
  CombinaisonB combination_2;

  Manager<AA> a;
  Manager<BB> b;

  a.init(&combination_1);
  b.init<&combination_2);

  return 0;
}

編集:OPが以下のコメントで気づいたように、マネージャーでの組み合わせの混合使用を禁止するための2番目の解決策。今、私は std::is_same を使用して「コンセプト」コントラクトをチェックしています。

template <Base B, typename DependentProperty<B>::Dependent D>
class Combination {
 public:
    typedef typename DependentProperty<B>::Dependent DependencyType;
  void reset() {}
  int o;
};

template <Base B>
class Manager {
 public:
    typedef typename DependentProperty<B>::Dependent DependencyType; 
    template <typename TCombinaison>
    void init(TCombinaison* t)
    {
        static_assert(std::is_same<TCombinaison::DependencyType, Manager::DependencyType>);
        t->reset();
    }

};
于 2013-01-25T10:18:58.217 に答える
0

継承を組み合わせて定数テンプレート パラメーターを使用しない場合は、Combination を拡張してそのテンプレート引数に関する情報を提供します。これをコンパイルしたくないことを考慮して、コードをコンパイルすることができます。

b.init(&combination_1);

マネージャー内の init メンバー テンプレートの組み合わせの型を間接的に指定して修正しようとしていますが、それが関数の唯一のパラメーターであり、型 si が main 内で定義されているため、init テンプレートがそれを推測します。 .

コンビネーションを使用して init を直接テンプレート化することを検討しますか?

このように、init() 宣言以外はすべて同じままで、コードは最初に望んだとおりにコンパイルされます。

class Base
{
};

class AA
:
    public Base
{
};

class BB
: 
    public Base
{
};

class Dependent1
{
};

class PP
:
    public Dependent1
{};

class Dependent2
{};

class XX
:
    public Dependent2
{};

template<class Base>
struct DependentProperty {
};

template<>
struct DependentProperty<AA> {
  typedef Dependent1 Dependent;
};

template<>
struct DependentProperty<BB> {
  typedef Dependent2 Dependent;
};

template <class Base> 
class Combination {
 public:

     typedef Base CombinationBase;
     typedef typename DependentProperty<Base>::Dependent CombinationDependent;

     void reset() 
     {

     }

     int o;
};


template <class Base>
class Manager
{
    public:

        // Any type C
        template<class C>
        void init (C* t)
        {
            // Any type C conforming to the implicit interface holding reset()
            t->reset(); 
            // Forcing specific combination
            Base b = typename C::CombinationBase(); 
            // Forcing it again
            typename DependentProperty<Base>::Dependent d = typename C::CombinationDependent();
        }
};

int main(int argc, char** argv) {

  Combination<AA> combination_1;
  Manager<AA> a;
  a.init(&combination_1);

  Manager<BB> b;
  Combination<BB> combination_2;
  b.init(&combination_2);

  b.init(&combination_1);

  return 0;
}

この場合、Combination テンプレートを拡張して、そのテンプレート パラメーターへのアクセスをクライアント コードに提供できます。もちろん、この場合のテンプレート C は、init メンバー関数内での実装 (保存されたテンプレート引数値へのアクセスなど) に依存するとすぐに、組み合わせの概念を改良したものになります。

于 2013-01-25T13:09:31.597 に答える
0

関数呼び出し部分を除いて、コードは正しいです。

a.init<PP, Combination>( &combination_1 );
b.init<XX, Combination> ( &combination_2 );

これはコンパイルされ、平和的に実行されます。

于 2015-07-10T06:40:39.897 に答える
-1

私が見る唯一のものは

template <typename DependentProperty<B>::Dependent D,
          template<Base, <-- wrong
                typename DependentProperty<B>::Dependent <-- wrong
          > class T>
void init(T<B, D>* t);

あなたのクラスはCombinationをテンプレート パラメーターとして待機していますが、を与えたいと考えています。

私はそれを修正するのに少し時間を費やしました-そのように

template <typename DependentProperty<B>::Dependent D,
          template<Base BB,
                typename DependentProperty<BB>::Dependent DD
          > class T>
void init(T<B, D>* t);

および他の多くの亜種がありましたが、成功しませんでした。

回答として並べてすみませんが、コメントにそれほど多くのコードを入力できませんでした

于 2013-01-25T11:05:21.563 に答える