19

I have trouble using std::begin() and std::end() (from the iterator library) with c-style array parameters.

void SetOrigin(const double i_point[3]) {
  Vector v;
  std::copy(
    std::begin(i_point), 
    std::end(i_point), 
    v.begin());
  this->setOrigin(v);
}

This results in the following error with Visual Studio 2010 (and similar for end):

error C2784: '_Ty *std::begin(_Ty (&)[_Size])' : could not deduce template argument for '_Ty (&)[_Size]' from 'const double []'
1>          c:\program files (x86)\microsoft visual studio 10.0\vc\include\xutility(995) : see declaration of 'std::begin'

Changing the parameter to non-const gives same result.

Trying to specify the parameter as

...
std::begin<const double, 3>(i_point), 
std::end<const double, 3>(i_point),
...

Gives:

error C2664: '_Ty *std::begin<const double,3>(_Ty (&)[3])' : cannot convert parameter 1 from 'const double []' to 'const double (&)[3]'

Is it just not possible to use std::begin on array parameters because they decay to pointers? Is there a trick to get around this or is it best just to not use the iterator functions on array parameters?

3
  • i_point[3] behaves like a flat pointer, rather than a real array, when used as a function parameter. Try the same with a local variable of array type, it should work. Commented Nov 18, 2013 at 22:48
  • 2
    const double i_point[3] this as a function parameter will decay to const double *i_point, you could use const double (&i_point)[3] Commented Nov 18, 2013 at 22:48
  • (for functions that want an array, just pass a pair of pointers) Commented Nov 18, 2013 at 22:49

6 Answers 6

29

Yes, std::begin and std::end can work with parameters that are C style arrays.

The trick is in passing a parameter that's a C style array. When you specify a 1D array as a normal parameter to a normal function, its type is silently adjusted from "array of T" to "pointer to T". When you call that function, what gets passed isn't the array (as an array), but a pointer to the first element of the array.

It is, however, possible to pass an array by reference to a function template:

template <class T, size_t N>
void function(T (&array)[N]) {
   // function body here
}

In this case, where you're passing an actual array (albeit, by reference) rather than a pointer, you can use std::begin and std::end perfectly well. For example:

template <class T, size_t N>
T sum(T (&array)[N]) { 
    return std::accumulate(std::begin(array), std::end(array), T());
}

Now passing an array is trivial, such as:

int array[] = {1, 2, 3, 4};

auto total = sum(array);

std::begin and std::end themselves are (or at least can be) implemented similarly to sum--the array is passed by reference, so they can look something like this:

template <class T, size_t N>
T *begin(T (&array)[N]) { 
    return array; 
}

template <class T, size_t N>
T *end(T (&array)[N]) {
    return array + N;
}

Note that although these were added to the standard more recently, they don't require any particularly tricky use of templates, so the implementation above should work fine with a plain old C++98 compiler (and, if memory serves, even with pre-standard compilers such as VC++ 6).

Sign up to request clarification or add additional context in comments.

Comments

5

First off, note that the parameter declaration const double i_point[3] is absolutely equivalent to const double* i_point. That is, the function takes any pointer to double const independent of the number of elements pointed to. As a result, it doesn't know the size and std::begin() and std::end() can't deduce the size (well, std::begin() doesn't really need to deduce the size anyway).

If you really want to use std::begin() and std::end() you need to pass an array with three element or a reference to such a beast. Since you cannot pass arrays by value, your best bet is to pass it by reference:

void SetOrigin(double const (&i_point)[3]) {
    // ...
}

This function only accepts arrays with exactly three elements as arguments: You cannot pass a pointer to three doubles or a part of a bigger array. In return, you can now use std::begin() and std::end().

Comments

1
void SetOrigin(const double i_point[3])

is as same as

void SetOrigin(const double i_point[])

or

void SetOrigin(const double *i_point)

So, std::begin and std::end can not accept it. In C++ you can not pass an array but as a pointer or reference. If it's a pointer then it doesn't carry any information of passed array.

Your alternatives are std::vector or std::array.

1 Comment

@chris: Totally forgotten that. I've just mentioned that. Thanks.
0

Have you looked at std::array?

It works better with other STL components

4 Comments

Precision is everything: std::array (note the a vs. A) ;-)
@DietmarKühl Wouldn't that be more a question of accuracy than precision?
Indeed I have, but simply want an interface for someone who wants to call my function with a c-style array as the argument. I have another version that accepts std::array.
@GlennTeitelbaum: touché! English isn't my native language and I'm barely capable of expressing myself.
0

While not directly answering your question (it has already been answered sufficiently by M M. and Dietmar Kühl), you appear to want to initialize some std::vector in this function. That said, why not just have:

std::vector v;
std::copy(std::begin(x), std::end(x), std::back_inserter(v));
// or std::copy(x, x + 3, std::back_inserter(v));

Instead of a function call to your function that is trying to do this?

Alternatively, you could write you function like this:

template<typename RandomIterator>
void SetOrigin(RandomIterator start, RandomIterator end)
{
    std::vector<int> v;
    std::copy(start, end, std::back_inserter(v));
    SetOrigin(v);
}

and then call it using:

double xyz[3];
SetOrigin(std::begin(xyz), std::end(xyz));

1 Comment

In this case I am not attempting what you suspect, but I see your point if I were.
0

C++20 introduces std::span which makes dealing with raw C-style arrays much nicer in these cases where you can't change an existing function signature. This allows you to internally treat the array like an STL container, while allowing external clients continue to pass in raw arrays.

#include <algorithm>
#include <iterator>
#include <span>
#include <vector>

void SetOrigin(const double i_point[3]) {
  std::span i_points(i_point, 3);
  std::vector<double> v;
  std::ranges::copy(i_points, std::back_inserter(v));
  this->setOrigin(v);
}

std::span is a view-like container, making it an essentially zero-cost wrapper around the existing storage of the raw array it is viewing.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.