56

I often find myself wanting to copy the contents of arrays that have a constant size, I usually just write something along the lines of:

float a[4] = {0,1,2,3};
float b[4];

for(int i=0; i<4; i++){
    b[i]=a[i];
}

As of lately, I am writing a linear calculus library for educational purposes, and I was wondering if there was a better way to do it.

The first thing that came to my mind, was using memcpy:

memcpy(b, a, sizeof(float) * 4);

But this seems very c-like and error prone to me. I like having my errors at compile time, and this can get ugly for data types with non-trivial copy constructors, or if I forget to multiply with sizeof(datatype).

Since I am writing a math library that I am going to use intensively, performance is very important to me. Are the compilers today smart enough to understand that the first example is just copying a chunk of memory and optimize it to be as efficient as the second solution?

Perhaps there is a function in the standard library that can help me? Something new in c++11? Or should I just create a macro or a template function?

bobbaluba
  • 3,584
  • 2
  • 31
  • 45
  • *"But this seems very c-like and error prone to me"* - What's error prone about it? You know the size, you know the memory is valid... no error will occur. If you are dealing with POD types I would always just use `memcpy`. – Ed S. Sep 13 '12 at 00:37

5 Answers5

82

If you use std::array instead of a built-in array (which you should), it becomes very simple. Copying an array is then the same as copying any other object.

std::array<float,4> a = {0,1,2,3};
std::array<float,4> b = a;
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • 1
    +1 this is clearly the way to go in C++11 (and not the `std::copy()` methods, as they use run-time size and hence are less prone to optimisation using than compile-time size, which is the very point of using arrays (instead of `vector`) in the first place – Walter Sep 08 '12 at 20:38
  • 1
    @Walter: Actually, the methods using `std::copy`, as presented by Mysticial and hjbabs, both use a compile time constant. So they can be optimized in the exact same ways. – Benjamin Lindley Sep 08 '12 at 21:39
  • 4
    @BenjaminLindley you didn't get my point. The `std::copy()` function template accepts the size as a *run-time constant*. If that number is known at compile time, then the compiler *may* do some optimisation. With `std::array<>::operator=` this is spelled out at compile time: there is not run-time-size-dependent code to be optimised (or not). I prefer not relying on the compiler to do these types of optimisations. – Walter Sep 09 '12 at 09:33
  • 2
    I'm a little confused. This question is about C style arrays. Why is the solution using std::arrays? To me this should definitely NOT be the canonical answer to the question. – user650261 Dec 01 '16 at 20:44
  • 5
    @user650261: The question is not about C-style arrays. It is about constant sized arrays. One way to have a constant size array is to use a c-style array. Another way, which the OP apparently was not aware of before asking the question, is `std::array`. And as it turns out, `std::array` is superior for that purpose, in almost every single way. And in particular, it is much better at solving the very problem the OP was actually concerned about. – Benjamin Lindley Dec 01 '16 at 20:55
57

The C++03 way

Use std::copy():

float a[4] = {0,1,2,3};
float b[4];

std::copy(a,a + 4, b);

That's about as clean as it gets.

The C++11 way

std::copy(std::begin(a), std::end(a), std::begin(b));

If you can use std::array

With std::array you just do simple assignment:

std::array<float,4> a = {0,1,2,3};
auto b = a;
Shital Shah
  • 63,284
  • 17
  • 238
  • 185
Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • 6
    To avoid things quietly breaking if you change the array size, I suggest `std::copy(a, a + sizeof(a)/sizeof(a[0]), b);`. Even better, wrap the `sizeof()` junk in a macro -- or even betterer, use a function template instead: `template size_t size((T&)[N]) { return N; }` – j_random_hacker Sep 08 '12 at 07:29
  • 20
    @j_random_hacker: In C++11, `std:copy(std::begin(a), std::end(a), std::begin(b))`. – GManNickG Sep 08 '12 at 08:39
  • 1
    @GManNickG: So they finally added generic `begin()` and `end()` in C++11? Wonderful! (And such an obvious thing... How did C++03 miss it?!) I can stop carting around my homebrewed ones :) – j_random_hacker Sep 08 '12 at 08:46
  • Interesting. I also didn't know you could use `std::begin/end` on arrays now. :) – Mysticial Sep 08 '12 at 08:51
  • 3
    Downvoters care to comment? It's hard to improve something if I don't know what's wrong. – Mysticial Sep 13 '12 at 00:20
  • I think it might have been the use of magic numbers as commented before. I have edited to reflect the comment thread – sehe Sep 13 '12 at 00:31
  • Thanks for edit. I wasn't sure if it was right to take it from the comments. – Mysticial Sep 13 '12 at 00:50
  • @Mysticial I couldn't get `std::copy(std::begin(a), std::end(a), std::begin(b));` to work on an array of `int` primitives and on the latest version of g++ using C++14 standard. Instead I had to use your first recommendation, `std::copy(a, a + x, b);` and that worked. – programmar Nov 05 '16 at 11:55
  • People downvoting because there is no copy of `std::array`, it's an assignment ctor -> answer is not correct. – Dmitry Sazonov Aug 14 '23 at 21:17
22

For interested in C++03 (and also C) solution - always use struct containing an array instead of solely array:

struct s { float arr[5]; };

Structs are copyable by default.

Its equivalent in C++11 is,already mentioned, std::array<float,5>;

PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • 2
    Good point about c++03 struct wrapping. I need to remember this better :) – sehe Sep 13 '12 at 00:32
  • 1
    thank you this is the info i was looking for from google. Because nothing much is said about assignation of std/boost::array. its default generated, therefore the standard dictates behavior. but chapters about aggregate are hard to read in santardese legal jargon. – v.oddou Apr 23 '15 at 05:11
6

Below method works for usual arrays as well std:array.

float a[4] = {0,1,2,3};
float b[4];

std::copy(std::begin(a), std::end(a), b);
Shital Shah
  • 63,284
  • 17
  • 238
  • 185
  • 2
    Note sure why downvoting. This works, its indeed preferred way in C++, applicable to usual arrays as well and I use it all the time. – Shital Shah Nov 28 '17 at 05:54
  • 1
    @Shitai Shah, preferred or not it's ugly and easy to error prone. – CodingLab Mar 25 '20 at 12:50
  • 2
    @CodingLab, disagree. My code is littered with this and it is a fine solution if you are using c-style arrays which are abundant out there in the real world. – danelliottster Mar 28 '20 at 18:22
  • How can this be uglier than `std::array a = {0,1,2,3};` ? I prefer this and use it in both C and C++. Looks cool and simple. Unfortunately, older c++ versions (< C++11) don't support this for member initialisation within class declarations. – Arundale Ramanathan Mar 17 '23 at 12:30
3
#include <algorithm>
std::copy_n(a, 4, b)
hjbabs
  • 27
  • 3