5

これは私の(ストリップされた)クラスと1つのオブジェクトのインスタンス化です:

template <typename T, typename Allocator = std::allocator<T> >
class Carray {
    typedef typename Allocator::size_type size_type;

    // ...

    explicit Carray(size_type n, const T& value, const Allocator& alloc = Allocator()) {
        // ...
    }

    template<typename InputIterator>
    Carray(InputIterator first, InputIterator last, const Allocator& alloc = Allocator()) {
        // ...
    }

    // ...
}

Carray<int> array(5, 10);

これがコンストラクターを呼び出すことを期待しますCarray(size_type, const T&, const Allocator&)が、そうではありません。どうやらこれはに決心しtemplate<typename InputIterator> Carray(InputIterator, InputIterator, const Allocator&)ます。

これを意図したとおりに機能させるには、何を変更する必要がありますか?std::vector<int> v(5, 10)の呼び出しは完全にうまく機能するので、私もそれが奇妙だと思います。そして、GCCの実装でコンストラクターの定義を見ると、次のことがわかります(コンパイラー実装名の名前を次のように変更しました __n)。

template<typename T, typename A = std::allocator<T> >
class vector {
    typedef size_t size_type;
    typedef T value_type;
    typedef A allocator_type;

    // ...

    explicit vector(size_type n, const value_type& value = value_type(), const allocator_type& a = allocator_type());

    template<typename InputIterator>
    vector(InputIterator first, InputIterator last, const allocator_type& a = allocator_type());

    // ...
};

同じようです。

4

4 に答える 4

7

明示的なコンストラクターは、size_tとintを想定しています。2つのintを提供しました。

を置き換えるintInputIterator、テンプレートがより適切に一致します。

標準コンテナを詳しく見ると、テンプレートメタプログラミングを使用して、InputIteratorが実際のイテレータであるか、整数型であるかを判断していることがわかります。次に、これは別の構造にリダイレクトされます。

編集
これを行う1つの方法は次のとおりです。

  template<class _InputIterator>
  vector(_InputIterator _First, _InputIterator _Last,
         const allocator_type& _Allocator = allocator_type() )
     : _MyAllocator(_Allocator), _MyBuffer(nullptr), _MySize(0), _MyCapacity(0)
  { _Construct(_First, _Last, typename std::is_integral<_InputIterator>::type()); }

private:
  template<class _IntegralT>
  void _Construct(_IntegralT _Count, _IntegralT _Value, std::true_type /* is_integral */)
  { _ConstructByCount(static_cast<size_type>(_Count), _Value); }

  template<class _IteratorT>
  void _Construct(_IteratorT _First, _IteratorT _Last, std::false_type /* !is_integral */)
  { _Construct(_First, _Last, typename std::iterator_traits<_IteratorT>::iterator_category()); }

コンパイラにstd::type_traitsがない場合は、boost::type_traitsを使用することもできます。

于 2011-05-18T20:17:39.407 に答える
3

これを試して。2つのintが渡された場合、イテレータコンストラクタは考慮から除外されます。

template<typename InputIterator>
Carray(InputIterator first, InputIterator last,
    const Allocator& alloc = Allocator(),
    typename boost::disable_if<boost::is_integral<InputIterator> >::type* dummy = 0) {
}

参照:http ://www.boost.org/doc/libs/1_46_1/libs/utility/enable_if.html


編集:「C ++ 03 STLだけで、ブーストなしの方法はありますか?」

std::type_traitsがC++03にあるかどうかはわかりません。私は常にブーストを利用できるので、それを使用します。しかし、あなたはこれを試すことができます。この特定のケースでは機能しますが、必要な一般性がない場合があります。

template <class T> class NotInt { typedef void* type; };
template <> class NotInt<int> { };

template <typename T, typename Allocator = std::allocator<T> >
class Carray {
  ...
  template<typename InputIterator>
  Carray(InputIterator first, InputIterator last,
      const Allocator& alloc = Allocator(),
      typename NotInt<InputIterator>::type t = 0) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
  }
};
于 2011-05-18T20:35:07.503 に答える
2

これは、すべてのイテレータタイプ(ポインタを含む)と現在の標準で機能するはずです。

#include <iostream>
#include <iterator>
#include <vector>

// uses sfinae to determine if the passed in type is indeed an iterator
template <typename T>
struct is_iterator_impl
{
  typedef char yes[1];
  typedef char no[2];

  template <typename C>
  static yes& _test(typename C::iterator_category*);

  template <typename>
  static no& _test(...);

  static const bool value = sizeof(_test<T>(0)) == sizeof(yes);
};

template <typename T, bool check = is_iterator_impl<T>::value>
struct is_iterator
{
  typedef void type;
};

template <typename T>
struct is_iterator<T, false>
{
};

template <typename T>
struct is_iterator<T*, false>
{
  typedef void type;
};

template <typename T>
struct foo
{
  explicit foo(std::size_t n, const T& value) 
  {
    std::cout << "foo:size_t" << std::endl;
  }

  template<typename InputIterator>
  foo(InputIterator first, InputIterator last, typename is_iterator<InputIterator>::type* dummy = 0) 
  {
    std::cout << "foo::iterator" << std::endl;
  }
};

int main(void)
{
  // should not cause a problem
  foo<int> f(1, 2);

  // using iterators is okay
  typedef std::vector<int> vec;
  vec v;
  foo<int> b(v.begin(), v.end());

  // using raw pointers - is okay
  char bar[] = {'a', 'b', 'c'};
  foo<char> c(bar, bar + sizeof(bar));
}

説明、イテレータは通常、などのいくつかのタイプを定義する必要がありiterator_category、これとsfinaeを利用して実際のイテレータを検出できます。複雑なのは、ポインターもイテレーターですが、これらの型が定義されていないため(何かstd::iterator_traitsが特殊化されているため)、上記と同様のアプローチを取ります。渡された型がポインターの場合、デフォルトでイテレーターとして扱われます。 。このアプローチにより、整数型をテストする必要がなくなります。

デモを参照してください:http ://www.ideone.com/E9l1T

于 2011-05-18T22:22:24.757 に答える
0

最初のコンストラクターは「value」引数が参照によって渡されることを期待し、2番目のコンストラクターは最初の2つの値が値によって渡されることを期待します。私の経験では、C ++はこの区別について非常に厳密です。オブジェクトコンストラクターの2番目の引数として、整数値ではなく整数変数を渡してみてください。

于 2011-05-18T21:22:04.603 に答える