0

コンストラクターが相互に排他的な署名を持つ、引数クラスとして受け取るテンプレートを作成するにはどうすればよいですか?

class A
{
  A(){};
public:
  int a;
  A(int i) : a(i) {};
};

class B{
  B(){};
public:
  int a,b;
  B(int i,int j) : a(i), b(j) {};
};


template <class T> class C {
public:
  T* _t;
  C(int i[])
  {                       //???
    _t=new T(i[0]);       //if T has T(int) signature
    _t=new T(i[0],i[1]);  //if T has T(int,int) signature
  }
  ~C() {delete _t;}
};

int main()  
{ 
  int Ai[]={1,2};
  C<A> _c(Ai);  // template should work instantiated with A and B
  C<B> _c(Ai);  // 
  return 0;
}

Aおよびの署名Bは固定されています(たとえば、int []に変更することはできません)。コンテキスト:(特殊な)コンテナータイプをテンプレート引数として受け取るラッパー(、、、など)について考えています。T=vector<int>このT=map<int,int>問題は、コンストラクターを呼び出す必要があるときに発生します。

4

2 に答える 2

4

変数ごとにテンプレート化されたコンストラクターを使用します。

template <typename T> struct C
{
    template <typename ...Args> C(Args &&... args)
    : _t(new T(std::forward<Args>(args)...))
    {
    }

    // ... destructor? Rule of five? Don't use pointers!!!
private:
    T * _t; // ouch!
};

使用法:

C<A> x(1);
C<B> y(2, 3);

(実際のプログラマーはもちろんstd::unique_ptr<T> _t;、セマンティクスが変更されていない member を好みますが、すべてのコメントを無視できます。)

于 2012-04-06T19:40:31.477 に答える
0

Kerrek SB の答えは部分的に正しいと思いますが、不完全です。C<T>のコンストラクターが過度に一般的であるという点で失敗します。つまりC<T>、コンストラクタ宣言を見るだけで、何でも構築できます。コンストラクターを選択してインスタンス化するまで、それ以外のことはわかりません。そして、それでは手遅れです。

具体例:

それが持っているとしましょうC<T>

friend bool operator<(const C&, const C&);

C<T>そして今、あなたは鍵を作りたいと思っていますmap:

std::map<C<A>, int> m;
// ...
m.erase(m.begin());

erase次のように見える2 つのオーバーロードがあるため、これはエラーです。

iterator  erase(const_iterator position);
size_type erase(const key_type& k);

m.begin()あり、iteratorです。これは、と(別名)iteratorの両方に同じように簡単に変換されます。const_iteratorkey_typeC<A>

これは、次のように呼び出すことで修正できます。

m.erase(m.cbegin());

代わりは。しかし、これは過度にジェネリックなコンストラクターが引き起こす問題の氷山の一角にすぎません。たとえば、分岐するコードは次のとおりです。

std::is_constructible<C<A>, any type and any number of them>::value

上記は常にtrue を返すため、誤検知が発生する可能性があります。

修正は少し面倒ですが、非常に実用的です。

template<typename T>
struct C
{
    template <class ...Args,
              class = typename std::enable_if
                      <
                         std::is_constructible<T, Args...>::value
                      >::type
             >
    C(Args&& ...args)
        : _t(new T(std::forward<Args>(args)...))
    {
    }
    // ...
};

つまり、コンストラクターが機能しない場合はインスタンス化されないように、コンストラクターに制約を追加します。これは厄介で、醜いです。おそらく、マクロでドレスアップしたいでしょう。罰金。しかし、このクラスは、上記の例で壊れている場所で機能します(そして、何年にもわたってバグレポートが一度に 1 つずつ発生する傾向がある他の多くの問題)。

ここで生のポインターを使用することに関する Kerrek SB の良いアドバイスに加えて、次のunique_ptr<T>ことを追加したいと思います。

  1. このコンストラクターはexplicit、少なくとも実際のユースケースで実際に暗黙的である必要があることが示されるまでは、おそらく である必要があります。

  2. Tへの(おそらくスマートな)ポインタの代わりにを格納することを検討してくださいT。ランタイム ポリモーフィズムを実現するために基本クラスを実際に指そうとしない限り、ポインター セマンティクスは必要ありません。

要約すると、過度にジェネリックなコードには用心し、過度にジェネリックなコンストラクターには完全に妄想的です。

于 2012-05-28T01:46:04.103 に答える