'C++: What is the correct cast for offseting to std::vector iterator?

I have a function that takes a std::vector of doubles, and copies them to another vector, but at a particular offset (assume there is sufficient space):

void copy_stuff(const std::vector<double> & data,
                std::vector<double> & dest,
                size_t dest_offset) {
    std::copy(data.begin(), data.end(), dest.begin() + dest_offset);
}

This results in a C++11 clang compiler -Weverything warning centered on the + dest_offset part:

Implicit conversion changes signedness: 'size_t' (aka 'unsigned long') to 'difference_type' (aka 'long').

I'm not sure how I should cast the expression dest.begin() + dest_offset in order to eliminate this warning. Casting the result to a double * does not compile:

    std::copy(data, data + data_size, static_cast<double *>(dest.begin() + dest_offset));

Cannot cast from type 'std::__1::__wrap_iter' to pointer type 'double *'.

I had considered using vector indexing and then taking the address:

    std::copy(data, data + data_size, &dest[dest_offset]);

This seems to eliminate the warning in this case, but doesn't compile if I try to use the same pattern with the source vector, i.e. with an offset involved with the first or second parameter of std::copy. For example:

static void copy_stuff_differently(const std::vector<double> & data,
                                   std::vector<double> & dest,
                                   size_t offset) {
    std::copy(data.begin() + offset, data.end(), dest.begin());
}

Gives the same original warning of implicit conversion on the + offset. Attempting to use the address-of-index might suggest:

    std::copy(&data[offset], data.end(), dest.begin());

Or a different but similar case:

    std::copy(data.begin(), &data[offset], dest.begin());

However both cause a similar error:

test.cpp:8:3: error: no matching function for call to 'copy'
  std::copy(&data[offset], data.end(), dest.begin());
  ^~~~~~~~~
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:1286:48: note:
      candidate template ignored: deduced conflicting types for parameter '_Ip' ('const double *' vs.
      'std::__1::__wrap_iter<const double *>')
    template <class _Ip, class _Op> friend _Op copy(_Ip, _Ip, _Op);
                                               ^

I'm looking for a consistent and warning-free way to handle such offsets. What is the right way to handle offsets into a vector and avoid such errors and warnings?



Solution 1:[1]

I'm not sure how I should cast the expression dest.begin() + dest_offset in order to eliminate this warning.

The warning is just telling you that dest_offset is expected to be of the type std::vector::difference_type, but it's size_t.

You can do the conversion explicitly to eliminate the warning (note that if the source value can't be represented in difference_type the result is implementation-defined). e.g.

dest.begin() + static_cast<std::vector<double>::difference_type>(dest_offset)

Or declare the parameter dest_offset with the type difference_type from the beginning.

Note that std::vector::difference_type is a signed integer type (usually std::ptrdiff_t), which is different from size_t; which is an unsigned integer type.

Solution 2:[2]

You can use some difference_type to avoid this warning all together:

#include<vector>

using difference_type = std::vector<double>::difference_type;
    
void copy_stuff(const std::vector<double> & data,
                std::vector<double> & dest,
//                size_t dest_offset)
                difference_type dest_offset)
{
    std::copy(data.begin(), data.end(), dest.begin() + dest_offset);
}

It is widely accepted nowadays [citation needed] that reporting/using sizes and some offsets as unsigned integer types was a mistake in the design of the Standard Library. So in my opinion the best is to not follow that convention and cast unsigned types from the standard library as soon as possible instead of adapting your function to use unsigned types.

For example copy_stuff(a.begin(), a.end(), static_cast<difference_type>(other.size()) ).


UPDATE 2022: The Ranges library in C++20 (std::ranges) has a function called std::ranges::ssize that will give the size of the contained as a signed integer type. This way it will be easier to extract sizes an have a consistent use of signed type for any subsequent arithmetic operation.

For example copy_stuff(a.begin(), a.end(), std::ranges::ssize(other) ).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2