Copy array in constexpr constructor
Asked Answered
T

3

8

I wrote a class with a constexpr copy constructor. (It is a struct in example to make it simpler.) One of the fields is an array. I want copy it too.

struct Foo
{
    static constexpr int SIZE = 4;
    constexpr Foo() = default;
    constexpr Foo(const Foo &foo) :
            arr{foo.arr[0], foo.arr[1], foo.arr[2], foo.arr[3]},
            bar(foo.bar+1) {}
    int arr[SIZE] = {0, 0, 0, 0};
    int bar = 0;
};

My version works but it isn't scalable. If I change SIZE, I have to modify the constructor. In addition, code looks ugly.

Is it any better way to copy array in constructor? Constructor must be constexpr.

Taneka answered 22/11, 2014 at 23:29 Comment(7)
Doesn't the default copy constructor do the job?Utu
Does not constexpr guarantee that the array will not be mutated? Why bother copy by value and not just a pointer?Alloway
@Utu I honestly do not crossed my mind. But the question is the question. You may assume that the class contains some fields that require some calculations for copying. Besides possible that it will be in the future.Taneka
Then you should post some code that illustrates the problem you're trying to solve.Utu
If you need some things differently from the compiler-generated copy constructor, but do want to copy each of the array's elements by value, then my suggestion would be to not use a raw array. Wrap that in a different structure instead, and use the compiler-generated copy constructor for that different structure. (In other words, use std::array.)Jory
@Utu As you wish.Taneka
The way to do it manually is make_index_sequence and delegating to a template constructor that does a pack expansion. make_index_sequence is C++14 but implementable in C++11.Teaching
A
3

You can use std::array. Since it is an aggregate type I believe this will work.

Alloway answered 22/11, 2014 at 23:40 Comment(1)
This is a good answer, but I would like to know how you can do it directly.Taneka
P
4

In C++14 you can just use a loop to copy the array:

constexpr Foo(const Foo &foo)
    : bar(foo.bar + 1)
{
    for (int i = 0; i < SIZE; ++i)
        arr[i] = foo.arr[i];
}

That doesn't mean you should do it. I'd recommend to use std::array instead. For example, if arr is an array of some class type with non-trivial initialization, it would be default-initialized and then copied, thus wasting performance, instead of copy-initialization when using std::array and default copy constructor.

Peursem answered 22/11, 2014 at 23:47 Comment(1)
It's not a good general approach even in C++14 though: it assigns to each array element, rather than copy-constructing each array element. That doesn't matter when it's an array of int, but it does matter for other types.Jory
A
3

You can use std::array. Since it is an aggregate type I believe this will work.

Alloway answered 22/11, 2014 at 23:40 Comment(1)
This is a good answer, but I would like to know how you can do it directly.Taneka
E
2

you can do something like in C++11 to just copy the array

template <int LENGTH>
constexpr bool copy_array(const char (&from)[LENGTH + 1], char (&to)[LENGTH], int index)
{
    return index < LENGTH ?  (to[index] = from[index], copy_array(from, to, ++index)) : false;
}

constexpr char src[] = "ab";
char dest[2];
copy_array(src, dest, 0);

edited: And in your context, you might be able to do something like:

#include <iostream>
#include <type_traits>
#include <array>
#include <utility>

struct Foo
{
    static constexpr int SIZE = 4;
    constexpr Foo() = default;
    constexpr Foo(const Foo &foo) :
            arr{foo.arr},
            bar(foo.bar + 1) {}
    std::array<int, SIZE> arr = {{0, 0, 0, 0}};
    int bar = 0;
};

int main()
{
    constexpr Foo foo1;
    constexpr Foo foo2(foo1);

    std::cout << foo1.bar << std::endl;
    std::cout << foo2.bar << std::endl;

    return 0;
}
Evince answered 7/7, 2017 at 3:44 Comment(2)
Could you include a whole constructor definition in your example?Taneka
I tried in the context of a constexpr class constructor but it's not easy. and there's another way: template <int LENGTH> struct Foo { template <typename... Args> constexpr Foo(Args&&... args) : data{args} {} char data[LENGTH];};Evince

© 2022 - 2024 — McMap. All rights reserved.