1

Sequence containers need to have fill constructors and range constructors, i.e. these must both work, assuming MyContainer models a sequence container whose value_type is int and size_type is std::size_t:

// (1) Constructs a MyContainer containing the number '42' 4 times.
MyContainer<int> c = MyContainer<int>(4, 42);

// (2) Constructs a MyContainer containing the elements in the range (array.begin(), array.end())
std::array<int, 4> array = {1, 2, 3, 4};
MyContainer<int> c2 = MyContainer<int>(array.begin(), array.end());

Trouble is, I'm not sure how to implement these two constructors. These signatures don't work:

template<typename T>
MyContainer<T>::MyContainer(const MyContainer::size_type n, const MyContainer::value_type& val);

template<typename T>
template<typename OtherIterator>
MyContainer<T>::MyContainer(OtherIterator i, OtherIterator j);

In this case, an instantiation like in example 1 above selects the range constructor instead of fill constructor, since 4 is an int, not a size_type. It works if I pass in 4u, but if I understand the requirements correctly, any positive integer should work.

If I template the fill constructor on the size type to allow other integers, the call is ambiguous when value_type is the same as the integer type used.

I had a look at the Visual C++ implementation for std::vector and they use some special magic to only enable the range constructor when the template argument is an iterator (_Is_iterator<_Iter>). I can't find any way of implementing this with standard C++.

So my question is... how do I make it work?

Note: I am not using a C++11 compiler, and boost is not an option.

Ben Hymers
  • 25,586
  • 16
  • 59
  • 84

1 Answers1

1

I think you've got the solution space right: Either disambiguate the call by passing in only explicitly size_t-typed ns, or use SFINAE to only apply the range constructor to actual iterators. I'll note, however, that there's nothing "magic" (that is, nothing based on implementation-specific extensions) about MSVC's _Is_iterator. The source is available, and it's basically just a static test that the type isn't an integral type. There's a whole lot of boilerplate code backing it up, but it's all standard C++.

A third option, of course, would be to add another fill constructor overload which takes a signed size.

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • You're right, `_Is_iterator` isn't magic; I just assumed it was since Intellisense wouldn't show me its definition, which is what usually happens with magic functions :) After implementing `enable_if`, `remove_const`, `remove_volatile`, `remove_cv`, `integral_constant`, `true_type`, `false_type` and `is_integral` (all of which thankfully don't need magic), implementing a `is_iterator` becomes just `template is_iterator : public integral_constant::value> {};`. Thanks! – Ben Hymers Jan 10 '14 at 12:21