1
votes

Say that you have a class that has an array member:

class A
{
private:
    uint8_t length;
    uint8_t arr[10];
public:
    A(uint8_t length, const uint8_t array[]): length(length)
    {
        memcpy(arr, array, length);
    }
};

Then you have a global constant object and you want to use uniform initialization like this:

const A A_CONST{1,{23}};

In this case the compiler gives an error: "invalid conversion from 'int' to 'const uint8_t*"

But this works:

const uint8_t arr[]={23};
const A A_CONST{1,arr};

But then you have an unnecessary global constant "arr" also. I don't understand why the compiler can't cast {23} to const uint8_t* by looking at the constructor. Why is this so and is there a workaround?

2
If it suits your use case, you could remove the constructor and then use aggregate initialization (your initialization statement will work) - M.M
{23} is not a valid initializer for a pointer (and nor should it be) - M.M
However, it's possible to initialize the array "arr" like in the second example? - user785560
Your last example is correct, yes - M.M
I guess I would have liked to have some more insight on this. To me these initializations look exactly the same. I just don't see why the first initialization fails and the second doesn't. In the second one you just have an extra middle-man variable "arr". I think when the compiler sees the constructor, it should understand that the second uniform initialization parameter {23} is a constant array. - user785560

2 Answers

2
votes

The constructor you defined, is equivalent to

A(uint8_t length, const uint8_t *array)

This is, what the compiler is trying to tell.

To have an initialization as in your example, you must use either another type, or a std::initializer_list, e.g.

#include <algorithm>
#include <initializer_list>

A(const std::initializer_list<uint8_t> array)
    : length(array.size())
{
    std::copy(array.begin(), array.end(), arr);
}

Apart from that, don't use plain arrays or (length, pointer) arguments. Better use modern types like std::array or std::vector, this is less error prone and you get this kind of initialization for free.

0
votes

If you really need to use an array (instead of std::vector) you can use this example:

#include <cstring>
#include <cstdint>
#include <iostream>
#include <initializer_list>

class A
{
private:
  static const uint8_t MAX_LENGTH = 10;
  uint8_t length;
  uint8_t arr[MAX_LENGTH];
public:
  A(uint8_t _length, const uint8_t array[]): length(_length)
  {
    if (length > MAX_LENGTH) { // to prevent copying out of memory
      length = MAX_LENGTH;
    }
    std::memcpy(arr, array, length);
  }
  A(std::initializer_list<uint8_t> l): length(l.size())
  {
    if (length > MAX_LENGTH) { // to prevent copying out of memory
      length = MAX_LENGTH;
    }
    std::initializer_list<uint8_t>::iterator it = l.begin();
    for (uint32_t i = 0; (i < length) || (it == l.end()); ++i, ++it) {
      arr[i] = *it;
    }
  }
  uint8_t printAllForExample() const {
    for (uint32_t i = 0; i < length; ++i) {
      std::cout << (int)arr[i] << "  ";
    }
    std::cout << std::endl;
  }
  uint8_t getLengthForExample() const {
     return length;
  }
};

int main() {
  std::cout << "A_CONST_1:" << std::endl;
  const A A_CONST_1{23};
  A_CONST_1.printAllForExample();

  std::cout << "A_CONST_2:" << std::endl;
  const A A_CONST_2{};
  std::cout << (int)A_CONST_2.getLengthForExample() << std::endl;
  A_CONST_2.printAllForExample();

  std::cout << "A_CONST_3:" << std::endl;
  const A A_CONST_3{0,1,2,3,4,5,6,7,8,9};
  std::cout << (int)A_CONST_3.getLengthForExample() << std::endl;
  A_CONST_3.printAllForExample();

  std::cout << "A_CONST_4:" << std::endl;
  const A A_CONST_4{0,1,2,3,4,5,6,7,8,9,10,11,12};
  std::cout << (int)A_CONST_4.getLengthForExample() << std::endl;
  A_CONST_4.printAllForExample();

  uint8_t data[] = {30,40,50};
  std::cout << "A_CONST_5:" << std::endl;
  const A A_CONST_5(3, data);
  std::cout << (int)A_CONST_5.getLengthForExample() << std::endl;
  A_CONST_5.printAllForExample();
  return 0;
}