2

I'm trying to initialize a private member array of a class without using the STL (because it is not supported on the Arduino microcontroller platform I'm using). This means no std::array or std::initializer_list etc.

The following compiles correctly using gcc 5.4.0 and avr-gcc 4.9.2, but that seems to be a bug. Clang throws an error saying error: array initializer must be an initializer list (as expected).

Code

#include <iostream>
#define PRINTFN() std::cout << __PRETTY_FUNCTION__ << std::endl

class Object {
  public:
    Object(int number) : number(number) { PRINTFN(); }
    Object(const Object &o) : number(o.number) { PRINTFN(); }
    void print() { std::cout << "The number is " << number << std::endl; }

  private:
    const int number;
};

template <size_t N>
class ManyObjects {
  public:
    ManyObjects(const Object(&objects)[N]) : objects(objects) {}
    void print() {
    for (Object &object : objects)
        object.print();
    }

  private:
    Object objects[N];
};

int main() {
    ManyObjects<3> many = {{1, 2, 3}};
    many.print();
}

Output

Object::Object(int)
Object::Object(int)
Object::Object(int)
Object::Object(const Object&)
Object::Object(const Object&)
Object::Object(const Object&)
The number is 1
The number is 2
The number is 3

What is the proper way to initialize objects? Or is it just not possible with the given constraints?

3
  • do you allow Object to have assignment operator ? Commented Jul 19, 2018 at 11:52
  • @UmNyobe, sure, if necessary. Commented Jul 19, 2018 at 11:53
  • Can't you use ::boost::array or self-coded array wrapper? Commented Jul 19, 2018 at 12:13

3 Answers 3

1

You can use variadic templates:

In ManyObjects class:

template <typename... _Args>
ManyObjects(_Args&&... arguments) :
    objects { arguments... }
{
}

More here

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

3 Comments

This does indeed work, but it's less readable, and less friendly to the end user (the Arduino platform is aimed at beginners), because it doesn't separate the arguments of the list from any other parameters to the ManyObjects constructor. I guess I could wrap the class that stores the objects with my original class, but I'm not entirely convinced.
@tttapa, template <typename... _Args> ManyObjects (int arg1, char arg2, _Args&&... arguments) - if you want add some other parameters to the ManyObjects constructor.
Care with variadic template constructor, as ManyObjects(ManyObjects&) would match the template instead of the copy constructor.
0

Yes variadic templates work but it is a bit tricky :

template <size_t N>
class ManyObjects {
  public:
    template<typename T, typename ...Args>
    ManyObjects(const T& x, Args&&... args) : objects{x, args...}{}

  private:
    Object objects[N];
};

int main() {
    ManyObjects<3> many{1, 2, 3};
    ManyObjects<3> copymany{many};
    copymany.print();
}

For any fixed N it can be interpreted as :

template <size_t N=3>
class ManyObjects {
  public:
        ManyObjects(int x, int y, int z) : objects{x, y, z}{} 
...
};

What is at play here :

  1. Object cannot be default initialized due to the definition of the constructor Object(int)
  2. Object assignment operator is implicitly deleted because number is const
  3. Thus any array Object arr[N] must be explicitly initialized using an aggregate initialization.
  4. The only way I think of is to perform extended initialization via the variadic templates.
  5. To prevent matching the copy constructor you can specify the first argument outside the parameter pack. You loose the construction of size 0, which can be enabled with a template specialization.

     ManyObjects<0> noneof;
     noneof.print();
    

3 Comments

Care with variadic template constructor, as ManyObjects(ManyObjects&) would match the template instead of the copy constructor.
how would define the copy constructor ?
In fact the template variadic constructor should be SFINAE to exclude signature matching the copy/move constructor one: checking size and first argument type.
0

I ended up following the advice of VTT, and creating my own array wrapper.

I'd love to hear some feedback if there are things that I have to look out for, or possible bugs, etc.

#include <iostream>

class Object {
public:
  Object(int number) : number{number} {}
  void print() { std::cout << "The number is " << number << std::endl; }

private:
  const int number;
};

// -------------------------------------------------------------------------- //

template <class T, size_t N> class ArrayWrapper {
public:
    T &operator[](size_t index) { return data[index]; }
    const T &operator[](size_t index) const { return data[index]; }
    T *begin() { return &data[0]; }
    const T *begin() const { return &data[0]; }
    T *end() { return &data[N]; }
    const T *end() const { return &data[N]; }

  T data[N];
};

// -------------------------------------------------------------------------- //

template <size_t N> class ManyObjects {
public:
  ManyObjects(const ArrayWrapper<Object, N> &objects, const char *name)
      : objects{objects}, name{name} {}
  void print() {
    std::cout << name << std::endl;
    for (auto &object : objects)
      object.print();
  }

private:
  ArrayWrapper<Object, N> objects;
  const char *name;
};

// -------------------------------------------------------------------------- //

int main() {
    ManyObjects<3> many = {{1, 2, 3}, "Many"};
    many.print();
}

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.