Почему перегрузка конструктора разрешается неправильно? - PullRequest
5 голосов
/ 19 мая 2011

Это мой (раздетый) класс и экземпляр одного объекта:

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 ]

7 голосов
/ 19 мая 2011

Явный конструктор ожидает size_t и int.Вы предоставили два целых числа.

Замена int на InputIterator делает шаблон более подходящим.

Если вы посмотрите на стандартные контейнеры, вы увидите, что они используют какой-то шаблонметапрограммирование, чтобы определить, может ли InputIterator быть реальным итератором или целочисленным типом.Затем он перенаправляет на другую конструкцию.

Редактировать
Вот один из способов сделать это:

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

Вы также можете использовать boost :: type_traits, еслиу компилятора нет std :: type_traits.

3 голосов
/ 19 мая 2011

Попробуй это.Это исключит конструктор итератора из рассмотрения, если будут переданы два целых числа:

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 и без повышения?"или нет - у меня всегда есть буст, так что я просто использую его.Но вы можете попробовать это.Он будет работать в этом конкретном случае, но может не иметь требуемой общности:

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";
  }
};
2 голосов
/ 19 мая 2011

Это должно работать со всеми типами итераторов (включая указатели) и текущим стандартом.

#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, и вы можете воспользоваться этими сфине, чтобы обнаружить настоящих итераторов.Сложность состоит в том, что указатели также являются итераторами, но эти типы не определены (что-то std::iterator_traits обеспечивает специализацию), поэтому в приведенном выше подходе используется аналогичный подход: если переданный тип является указателем, то он по умолчанию обрабатываетсякак итератор.Такой подход избавляет вас от необходимости проверять целочисленные типы.

См. Демонстрацию: http://www.ideone.com/E9l1T

0 голосов
/ 19 мая 2011

Первый конструктор ожидает, что аргумент 'value' будет передан по ссылке, а второй конструктор ожидает, что первые 2 значения будут переданы по значению. По моему опыту, C ++ довольно строго относится к этому различию, попробуйте передать целочисленную переменную вместо целочисленного значения в качестве второго аргумента конструктору объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...