Lukasz Wiklendt
Lukasz Wiklendt

Reputation: 4468

General iterable type with specific element type

I'm trying to write a function for enumerating through a number of a specific base, where the number is stored in some kind of list. Here is an example, taking a std::vector

void next_value(std::vector<unsigned int> &num, unsigned int base) {
   unsigned int carry = 1;
   for (unsigned int &n: num) {
       n += carry;
       if (n >= base) {
           carry = 1;
           n = 0;
       } else {
           carry = 0;
       }
   }
}

The num vector doesn't necessarily need to be a vector, it can be an array, or actually any type that has a std::begin() and std::end() defined for it. Is there a way to express that num can be anything with begin() and end(), but that it must have unsigned int type for its elements?

Upvotes: 0

Views: 1048

Answers (3)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275385

I would not in your case.

Change carry to a book, use ++ instead of +=, make base a type T, and n an auto&.

Finally, return carry.

Your code now ducktypes exactly the requirements.

If you want diagnostics, static assert that the operations make sense with custom error messages.

This let's your code handle unsigned ints, polynomials, bigints, whatever.

Upvotes: 0

stefan
stefan

Reputation: 10345

Writing a generic function with a static_assert is a good idea, because you can give the user a helpful error message rather than "foo".

However there is another approach using C++11:

template <typename Container, typename ValueType>
typename std::enable_if<std::is_same<Container::value_type, ValueType>::value, void>::type
   next_value(Container& num, ValueType base)
{
  // ... 
}

This is a rather cryptic approach if you've never seen this before. This uses "Substitution failure is not an error" (SFINAE for short). If the ValueType doesn't match the Container::value_type, this template does not form a valid function definition and is therefore ignored. The compiler behaves as if there is not such function. I.e., the user can't use the function with an invalid combination of Container and ValueType.

Note that I do recommend using the static_assert! If you put a reasonable error message there, the user will thank you a thousand times.

Upvotes: 0

John Zwinck
John Zwinck

Reputation: 249133

If you really want to check this, try:

template <class Sequence>
void next_value(Sequence &num, unsigned int base) {
    static_assert(boost::is_same<Sequence::value_type, unsigned>::value, "foo");
    // ...

If you're not using C++11 yet, use BOOST_STATIC_ASSERT instead.

If you need to support plain C-style arrays, a bit more work is needed.

On the other hand, @IgorTandetnik correctly points out that you probably do not need to explicitly check at all. The compiler will give you an (ugly) error if you pass a type which is truly unusable.

Upvotes: 1

Related Questions